diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 8d8d32aa5..7c7046991 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -18,10 +18,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -600,7 +602,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC * in a burst. Rather than execute the same request repeatedly we can avoid queuing * up more requests if the previous request has not yet been started. */ - private Map> reconcileRequests = new ConcurrentHashMap<>(); + private Set reconcileRequests = Collections.synchronizedSet(new HashSet<>()); private DiagnosticSeverityProvider severityProvider = DiagnosticSeverityProvider.DEFAULT; @@ -616,21 +618,14 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC return; } - if (reconcileRequests.isEmpty()) { - busyReconcile = new CompletableFuture<>(); - } - String uri = docId.getUri(); - CompletableFuture newFuture = new CompletableFuture(); - CompletableFuture oldFuture = reconcileRequests.putIfAbsent(uri, newFuture); - if (oldFuture != null && oldFuture != newFuture) { + if (!reconcileRequests.add(uri)) { log.debug("Reconcile skipped {}", uri); return; } - - CompletableFuture reconcileSession = oldFuture == null ? newFuture : oldFuture; // Log.debug("Reconciling BUSY"); + CompletableFuture currentSession = this.busyReconcile = new CompletableFuture<>(); // Avoid running in the same thread as lsp4j as it can result // in long "hangs" for slow reconcile providers Mono mono = props.getReconcileDelay() > 0 @@ -710,10 +705,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC return Mono.empty(); }) .doFinally(ignore -> { - reconcileSession.complete(null); - if (reconcileRequests.isEmpty()) { - busyReconcile.complete(null); - } + currentSession.complete(null); // Log.debug("Reconciler DONE : "+this.busyReconcile.isDone()); }) .subscribe(); diff --git a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java index 63527a582..01d93cf05 100644 --- a/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java +++ b/headless-services/concourse-language-server/src/test/java/org/springframework/ide/vscode/concourse/ConcourseEditorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2019 Pivotal, Inc. + * Copyright (c) 2016, 2022 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -4806,14 +4806,16 @@ public class ConcourseEditorTest { @Test public void reconcilerRaceCondition() throws Exception { SynchronizationPoint reconcilerThreadStart = harness.reconcilerThreadStart(); + System.out.println("Reconcile thread started"); Editor editor = harness.newEditor("garbage"); - - reconcilerThreadStart.reached(); // Blocks until the reconciler thread is reached. + System.out.println("Editor created"); try { String editorContents = editor.getRawText(); for (int i = 0; i < 4; i++) { + System.out.println("Perfroming change: " + i); editorContents = "\n" +editorContents; editor.setText(editorContents); + System.out.println("Text changed: " + i); } } finally { reconcilerThreadStart.unblock();