LS: more extensive and async shutdown logic

The C# process did not terminate properly
This commit is contained in:
Michael Panchenko committed 2025-06-29 00:55:20 +02:00
1 parent 0c282c7f90
commit 365cb8e9d2
1 file changed
+36 -4
+36 -4
View File
@@ -6,6 +6,7 @@ import os
import pathlib
import pickle
import re
import subprocess
import threading
from abc import ABC, abstractmethod
from collections import defaultdict
@@ -343,15 +344,46 @@ class SolidLanguageServer(ABC):
# Stage 1: Graceful Termination Request
# Send LSP shutdown and close stdin to signal no more input.
try:
self.server.shutdown()
self.logger.log("Sending LSP shutdown request...", logging.DEBUG)
# Use a thread to timeout the LSP shutdown call since it can hang
shutdown_thread = threading.Thread(target=self.server.shutdown)
shutdown_thread.daemon = True
shutdown_thread.start()
shutdown_thread.join(timeout=2.0) # 2 second timeout for LSP shutdown
if shutdown_thread.is_alive():
self.logger.log("LSP shutdown request timed out, proceeding to terminate...", logging.DEBUG)
else:
self.logger.log("LSP shutdown request completed.", logging.DEBUG)
if process.stdin and not process.stdin.is_closing():
process.stdin.close()
except Exception:
pass # Ignore errors here, we are proceeding to terminate anyway.
self.logger.log("Stage 1 shutdown complete.", logging.DEBUG)
except Exception as e:
self.logger.log(f"Exception during graceful shutdown: {e}", logging.DEBUG)
# Ignore errors here, we are proceeding to terminate anyway.
# Stage 2: Terminate and Concurrently Drain stdout/stderr
# Stage 2: Terminate and Wait for Process to Exit
self.logger.log(f"Terminating process {process.pid}, current status: {process.poll()}", logging.DEBUG)
process.terminate()
# Stage 3: Wait for process termination with timeout
try:
self.logger.log(f"Waiting for process {process.pid} to terminate...", logging.DEBUG)
exit_code = process.wait(timeout=timeout)
self.logger.log(f"Language server process terminated successfully with exit code {exit_code}.", logging.INFO)
except subprocess.TimeoutExpired:
# If termination failed, forcefully kill the process
self.logger.log(f"Process {process.pid} termination timed out, killing process forcefully...", logging.WARNING)
process.kill()
try:
exit_code = process.wait(timeout=2.0)
self.logger.log(f"Language server process killed successfully with exit code {exit_code}.", logging.INFO)
except subprocess.TimeoutExpired:
self.logger.log(f"Process {process.pid} could not be killed within timeout.", logging.ERROR)
except Exception as e:
self.logger.log(f"Error during process shutdown: {e}", logging.ERROR)
@contextmanager
def start_server(self) -> Iterator["SolidLanguageServer"]:
self.start()