Spawn language servers in parallel (using threads)

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-10-28 18:52:23 +01:00
1 parent e0ab4db189
commit c7a0afb6ce
2 files changed
+48 -11

No files matched your search

+38 -10
View File
@@ -1,11 +1,13 @@
import json
import logging
import os
import threading
from collections.abc import Callable
from pathlib import Path
from typing import Any
import pathspec
from sensai.util.logging import LogTime
from sensai.util.string import ToStringMixin
from serena.config.serena_config import DEFAULT_TOOL_TIMEOUT, ProjectConfig, get_serena_managed_in_project_dir
@@ -405,15 +407,41 @@ class Project(ToStringMixin):
log.info(f"Creating language server manager for {self.project_root}")
language_servers: dict[Language, SolidLanguageServer] = {}
language_server_factories: dict[Language, Callable[[], SolidLanguageServer]] = {}
threads = []
exceptions = {}
lock = threading.Lock()
def start_language_server(language: Language, factory: Callable[[], SolidLanguageServer]):
try:
with LogTime(f"Language server startup (language={language.value})"):
language_server = factory()
language_server.start()
if not language_server.is_running():
raise RuntimeError(
f"Failed to start the language server for language {language.value} with root {self.project_root}"
)
with lock:
language_servers[language] = language_server
except Exception as e:
log.error(f"Error starting language server for language {language.value}: {e}", exc_info=e)
with lock:
exceptions[language] = e
# start language servers in parallel threads
for language in self.project_config.languages:
language_server_factory = self._create_language_server_factory(
language, ls_timeout, ls_specific_settings, log_level, trace_lsp_communication
)
language_server = language_server_factory()
log.info(f"Starting language server for {language} at root {self.project_root}")
language_server.start()
if not language_server.is_running():
raise RuntimeError(f"Failed to start the language server for {language} for root {self.project_root}")
language_servers[language] = language_server
language_server_factories[language] = language_server_factory
factory = self._create_language_server_factory(language, ls_timeout, ls_specific_settings, log_level, trace_lsp_communication)
language_server_factories[language] = factory
thread = threading.Thread(target=start_language_server, args=(language, factory), name="StartLS:" + language.value)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
# If any server failed to start up, raise an exception and stop all started language servers
if exceptions:
for ls in language_servers.values():
ls.stop()
failure_messages = "\n".join([f"{lang.value}: {e}" for lang, e in exceptions.items()])
raise Exception(f"Failed to start language servers:\n{failure_messages}")
return LanguageServerManager(language_servers, language_server_factories)
+10 -1
View File
@@ -1688,7 +1688,16 @@ class SolidLanguageServer(ABC):
return self
def stop(self, shutdown_timeout: float = 2.0) -> None:
self._shutdown(timeout=shutdown_timeout)
"""
Stops the language server process.
This function never raises an exception (any exceptions during shutdown are logged).
:param shutdown_timeout: time, in seconds, to wait for the server to shutdown gracefully before killing it
"""
try:
self._shutdown(timeout=shutdown_timeout)
except Exception as e:
self.logger.log(f"Exception while shutting down language server: {e}", logging.WARNING)
@property
def language_server(self) -> Self: