Skip to content

Commit

Permalink
Perform updates to Stop Language Server Timer synchronously. (#670)
Browse files Browse the repository at this point in the history
Multiple Timer threads can be left running if
LanguageServerWrapper.startStopTimer() runs in parallel with
LanguageServerWrapper.removeStopTimer() or another startStopTimer(). We
prevent this issue by reducing the Timer threads created down to one,
and then only scheduling or cancelling TimerTasks on that Timer thread
synchronously.

This should fix issue #644.
  • Loading branch information
andrewL-avlq authored Jun 1, 2023
1 parent 31bf765 commit 21d3a17
Showing 1 changed file with 23 additions and 17 deletions.
40 changes: 23 additions & 17 deletions org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) {
private LanguageServer languageServer;
private LanguageClientImpl languageClient;
private ServerCapabilities serverCapabilities;
private Timer timer;
private final Timer timer = new Timer("Stop Language Server Task Processor"); //$NON-NLS-1$
private TimerTask stopTimerTask;
private AtomicBoolean stopping = new AtomicBoolean(false);

private final ExecutorService dispatcher;
Expand Down Expand Up @@ -392,22 +393,28 @@ public boolean isActive() {
return this.launcherFuture != null && !this.launcherFuture.isDone() && !this.launcherFuture.isCancelled();
}

private void removeStopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
private void removeStopTimerTask() {
synchronized (timer) {
if (stopTimerTask != null) {
stopTimerTask.cancel();
stopTimerTask = null;
}
}
}

private void startStopTimer() {
timer = new Timer("Stop Language Server Timer"); //$NON-NLS-1$

timer.schedule(new TimerTask() {
@Override
public void run() {
stop();
private void startStopTimerTask() {
synchronized (timer) {
if (stopTimerTask != null) {
stopTimerTask.cancel();
}
}, TimeUnit.SECONDS.toMillis(this.serverDefinition.lastDocumentDisconnectedTimeout));
stopTimerTask = new TimerTask() {
@Override
public void run() {
stop();
}
};
timer.schedule(stopTimerTask, TimeUnit.SECONDS.toMillis(this.serverDefinition.lastDocumentDisconnectedTimeout));
}
}

/**
Expand All @@ -425,7 +432,7 @@ public synchronized void stop() {
if (alreadyStopping) {
return;
}
removeStopTimer();
removeStopTimerTask();
if (this.initializeFuture != null) {
this.initializeFuture.cancel(true);
this.initializeFuture = null;
Expand Down Expand Up @@ -558,7 +565,7 @@ private boolean supportsWorkspaceFolderCapability() {
* @noreference internal so far
*/
private @Nullable CompletableFuture<@NonNull LanguageServerWrapper> connect(@NonNull URI uri, IDocument document) throws IOException {
removeStopTimer();
removeStopTimerTask();
if (this.connectedDocuments.containsKey(uri)) {
return CompletableFuture.completedFuture(this);
}
Expand Down Expand Up @@ -601,8 +608,7 @@ public CompletableFuture<Void> disconnect(URI uri) {
}
if (this.connectedDocuments.isEmpty()) {
if (this.serverDefinition.lastDocumentDisconnectedTimeout != 0) {
removeStopTimer();
startStopTimer();
startStopTimerTask();
} else {
stop();
}
Expand Down

0 comments on commit 21d3a17

Please sign in to comment.