From 5d0bed47e4ff5a8d3f95d894963e2764e4173680 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 13 Apr 2022 16:42:07 +0200 Subject: [PATCH] GH-750 - introduce dedicated thread pool executors to avoid handing threads to block everything else --- .../util/SimpleTextDocumentService.java | 26 ++++++++++++------- .../v2/SpringProcessConnectorLocal.java | 11 +++++--- .../livehover/v2/SpringProcessDescriptor.java | 9 ++++--- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index d34557675..b5fa78b60 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -17,6 +17,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -90,6 +92,8 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE private final ListenerList documentCloseListeners = new ListenerList<>(); private final ListenerList documentOpenListeners = new ListenerList<>(); private List> documentSaveListeners = ImmutableList.of(); + + private final Executor messageWorkerThreadPool; private CompletionHandler completionHandler; private CompletionResolveHandler completionResolveHandler; @@ -104,6 +108,8 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE public SimpleTextDocumentService(SimpleLanguageServer server, LanguageServerProperties props) { this.server = server; this.props = props; + + this.messageWorkerThreadPool = Executors.newCachedThreadPool(); } /** @@ -253,7 +259,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE public CompletableFuture, CompletionList>> completion(CompletionParams position) { log.info("completion request arrived: " + position.getTextDocument().getUri()); - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { CompletionHandler h = completionHandler; if (h != null) { @@ -269,7 +275,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE public CompletableFuture resolveCompletionItem(CompletionItem unresolved) { log.info("Completion item resolve request received: {}", unresolved.getLabel()); - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { try { CompletionResolveHandler h = completionResolveHandler; if (h != null) { @@ -291,7 +297,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE public CompletableFuture hover(HoverParams hoverParams) { log.debug("hover requested for {}", hoverParams.getPosition()); - CompletableFuture result = CompletableFutures.computeAsync(cancelToken -> { + CompletableFuture result = CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { return computeHover(cancelToken, hoverParams); }); @@ -330,7 +336,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE DefinitionHandler h = this.definitionHandler; if (h != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { cancelToken.checkCanceled(); @@ -361,7 +367,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE ReferencesHandler h = this.referencesHandler; if (h != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { List list = h.handle(cancelToken, params); return list != null && list.isEmpty() ? null : list; }); @@ -376,7 +382,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE DocumentSymbolHandler h = this.documentSymbolHandler; if (h != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { cancelToken.checkCanceled(); try { @@ -433,7 +439,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE CodeLensHandler handler = this.codeLensHandler; if (handler != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { return handler.handle(cancelToken, params); }); } @@ -445,7 +451,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE CodeLensResolveHandler handler = this.codeLensResolveHandler; if (handler != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { return handler.handle(unresolved); }); @@ -476,7 +482,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE } } } - }); + }, messageWorkerThreadPool); } } @@ -484,7 +490,7 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE public CompletableFuture> documentHighlight(DocumentHighlightParams highlightParams) { DocumentHighlightHandler handler = this.documentHighlightHandler; if (handler != null) { - return CompletableFutures.computeAsync(cancelToken -> { + return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> { return handler.handle(cancelToken, highlightParams); }); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java index 35e63f3c6..b84b01034 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2020 Pivotal, Inc. + * Copyright (c) 2019, 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 @@ -20,6 +20,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -36,7 +38,6 @@ import com.sun.tools.attach.VirtualMachineDescriptor; */ @SuppressWarnings("restriction") public class SpringProcessConnectorLocal { - private static final Logger log = LoggerFactory.getLogger(SpringProcessConnectorLocal.class); @@ -47,12 +48,16 @@ public class SpringProcessConnectorLocal { private final Set processes; private final SpringProcessConnectorService processConnectorService; + private final Executor statusUpdateThreadPool; private boolean projectsChanged; + public SpringProcessConnectorLocal(SpringProcessConnectorService processConnector, ProjectObserver projectObserver) { this.projects = new ConcurrentHashMap<>(); this.processes = Collections.synchronizedSet(new HashSet<>()); + this.statusUpdateThreadPool = Executors.newFixedThreadPool(10); + this.projectsChanged = false; this.processConnectorService = processConnector; @@ -168,7 +173,7 @@ public class SpringProcessConnectorLocal { List> futures = new ArrayList<>(); for (SpringProcessDescriptor process : processes) { - futures.add(process.updateStatus(projects::containsKey, projects::get)); + futures.add(process.updateStatus(projects::containsKey, projects::get, statusUpdateThreadPool)); } CompletableFuture allStatusUpdates = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java index 69ab3bb03..3c2bf8fb3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2020 Pivotal, Inc. + * Copyright (c) 2019, 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 @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Properties; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.function.Predicate; import org.slf4j.Logger; @@ -57,7 +58,7 @@ public class SpringProcessDescriptor { private SpringProcessStatus status; private String projectName; - + public SpringProcessDescriptor(String processKey, String processID, String processName, VirtualMachineDescriptor vm) { this.processKey = processKey; this.processID = processID; @@ -109,11 +110,11 @@ public class SpringProcessDescriptor { return true; } - public CompletableFuture updateStatus(Predicate projectIsKnown, Predicate projectHasActuators) { + public CompletableFuture updateStatus(Predicate projectIsKnown, Predicate projectHasActuators, Executor statusUpdateThreadPool) { return CompletableFuture.supplyAsync(() -> { this.status = checkStatus(projectIsKnown, projectHasActuators); return null; - }); + }, statusUpdateThreadPool); } private SpringProcessStatus checkStatus(Predicate projectIsKnown, Predicate projectHasActuators) {