https://github.com/igorkudrin created https://github.com/llvm/llvm-project/pull/209988
Tests in `TestDAP_server.py` create `DebugAdapterServer`, passing a connection string. `DebugAdapterServer.__init__()` creates a socket and passes the associated input and output streams to its parent class, `DebugCommunication`. `DAPTestCaseBase.launch()`, which is called from `TestDAP_server.run_debug_session()`, adds a teardown hook that eventually calls `DebugCommunication.terminate()`. If the socket is not closed when this hook executes, it waits indefinitely for the receiving thread to join. In the normal case, when a test passes, the connection is closed by calling `self.dap_server.request_disconnect()` at the end of `TestDAP_server.run_debug_session()`. If a test fails, the cleanup hook is called while the connection is still active, which results in a hang. This can be reproduced by removing the call to `request_disconnect()` or by changing the condition in the `assertEqual()` on the previous line. The fix passes the opened socket to 'DebugCommunication' and closes the socket explicitly in 'DebugCommunication.terminate()'. >From e10c56e5d437246fd4332153719a7697bc889db8 Mon Sep 17 00:00:00 2001 From: Igor Kudrin <[email protected]> Date: Wed, 15 Jul 2026 22:20:48 -0700 Subject: [PATCH] [lldb-dap][test] Avoid a hang on a test failure Tests in 'TestDAP_server.py' create 'DebugAdapterServer', passing a connection string. 'DebugAdapterServer.__init__()' creates a socket and passes the associated input and output streams to its parent class, 'DebugCommunication'. 'DAPTestCaseBase.launch()', which is called from 'TestDAP_server.run_debug_session()', adds a teardown hook that eventually calls 'DebugCommunication.terminate()'. If at the moment this hook executes the socket is not closed yet, it waits indefinitely for the receiving thread to join. In the normal case, when a test passes, the connection is closed by calling 'self.dap_server.request_disconnect()' at the end of 'TestDAP_server.run_debug_session()'. If a test fails, the cleanup hook is called while the connection is still active, which results in a hang. This can be reproduced by removing the call to 'request_disconnect()' or by changing the condition in the 'assertEqual()' on the previous line. The fix passes the opened socket to 'DebugCommunication' and closes the socket explicitly in 'DebugCommunication.terminate()'. --- .../Python/lldbsuite/test/tools/lldb-dap/dap_server.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index c8acf67b23b99..8f710154c5da3 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -205,11 +205,13 @@ def __init__( send: BinaryIO, init_commands: Optional[List[str]] = None, spawn_helper: Optional[SpawnHelperCallback] = None, + socket: Optional[socket.socket] = None, ): self._log = Log() self.send = send self.recv = recv self.spawn_helper = spawn_helper + self.socket = socket # Packets that have been received and processed but have not yet been # requested by a test case. @@ -1643,6 +1645,8 @@ def request_custom(self, command: str, arguments: Optional[dict[str, Any]] = Non return self._send_recv(command_dict) def terminate(self): + if self.socket: + self.socket.shutdown(socket.SHUT_RDWR) self.send.close() if self._recv_thread.is_alive(): self._recv_thread.join() @@ -1706,6 +1710,7 @@ def __init__( s.makefile("wb"), init_commands, spawn_helper, + socket=s, ) self.connection = connection else: _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
