mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 22:14:54 +00:00
fix(gdscript): detect broken TCP connection and fail fast on send error (#1493)
fix(gdscript): detect broken TCP connection and fail fast on reconnect When the Godot editor restarts or the LSP connection drops, the TCPLanguageServer read loop exits but previously left self._sock set, causing is_running() to return True. Subsequent LSP requests would then send to a dead socket, log a broken-pipe error silently, and block for the full tool timeout (235 s by default) before raising. Two fixes in TCPLanguageServer: - _send_payload: on OSError, clear self._sock/self._file and immediately cancel all pending requests so callers fail fast instead of waiting out the timeout. - _read_loop: after unexpected exit, clear self._sock/self._file so is_running() returns False and _ensure_functional_ls can trigger an automatic reconnect on the next tool call. Also fixes GodotLanguageServer so that ls_specific_settings.request_timeout is respected as a cap on the per-request timeout. Previously the value was read in _create_language_server_interface but then overridden by the tool-timeout-derived value passed to set_request_timeout(). Now set_request_timeout() applies min(incoming, configured) so users can set a low request_timeout in their project.yml to fail fast without lowering the global tool_timeout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c7d5eef56
commit
87eaa40553
2 files changed
+16
No files matched your search
@@ -46,9 +46,17 @@ class GodotLanguageServer(SolidLanguageServer):
|
||||
else:
|
||||
log.warning("Could not detect Godot version for project at %s", repository_root_path)
|
||||
|
||||
self._configured_request_timeout: float | None = None
|
||||
|
||||
# Dummy ProcessLaunchInfo — _create_language_server_interface() ignores it
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=""), "gdscript", solidlsp_settings)
|
||||
|
||||
def set_request_timeout(self, timeout: float | None) -> None:
|
||||
"""Cap the timeout at the value configured in ls_specific_settings, if set."""
|
||||
if timeout is not None and self._configured_request_timeout is not None:
|
||||
timeout = min(timeout, self._configured_request_timeout)
|
||||
super().set_request_timeout(timeout)
|
||||
|
||||
@staticmethod
|
||||
def _detect_godot_version(repo_path: str) -> int | None:
|
||||
"""Read project.godot to determine the major Godot version.
|
||||
@@ -75,6 +83,7 @@ class GodotLanguageServer(SolidLanguageServer):
|
||||
settings: dict = self._custom_settings or {}
|
||||
port = settings.get("port", DEFAULT_GODOT_LS_PORT)
|
||||
request_timeout = settings.get("request_timeout", DEFAULT_GODOT_REQUEST_TIMEOUT)
|
||||
self._configured_request_timeout = settings.get("request_timeout")
|
||||
self._conn_info = TCPConnectionInfo(host="127.0.0.1", port=port)
|
||||
return TCPLanguageServer(
|
||||
connection_info=self._conn_info,
|
||||
|
||||
@@ -727,6 +727,9 @@ class TCPLanguageServer(LanguageServerInterface):
|
||||
sock.sendall(data)
|
||||
except OSError as e:
|
||||
log.error("Failed to write to TCP language server: %s", e)
|
||||
self._sock = None
|
||||
self._file = None
|
||||
self._cancel_pending_requests(LanguageServerTerminatedException("TCP send error", self.language, cause=e))
|
||||
|
||||
def _read_loop(self) -> None:
|
||||
"""Read Content-Length-framed LSP messages from the TCP socket and dispatch them."""
|
||||
@@ -775,3 +778,7 @@ class TCPLanguageServer(LanguageServerInterface):
|
||||
exception = LanguageServerTerminatedException("TCP language server read loop terminated unexpectedly", self.language)
|
||||
log.error(str(exception))
|
||||
self._cancel_pending_requests(exception)
|
||||
# Clear the socket so is_running() returns False, allowing _ensure_functional_ls
|
||||
# to detect the broken connection and restart the language server.
|
||||
self._sock = None
|
||||
self._file = None
|
||||
Reference in new issue
Block a user