Skip to content

[lldb-dap][test] Avoid a hang on a test failure#209988

Merged
igorkudrin merged 2 commits into
llvm:mainfrom
igorkudrin:lldb-dap-tests-avoid-hang
Jul 18, 2026
Merged

[lldb-dap][test] Avoid a hang on a test failure#209988
igorkudrin merged 2 commits into
llvm:mainfrom
igorkudrin:lldb-dap-tests-avoid-hang

Conversation

@igorkudrin

Copy link
Copy Markdown
Contributor

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()'.

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()'.
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lldb

Author: Igor Kudrin (igorkudrin)

Changes

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()'.


Full diff: https://github.com/llvm/llvm-project/pull/209988.diff

1 Files Affected:

  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (+5)
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:

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 34085 tests passed
  • 506 tests skipped

✅ The build succeeded and all tests passed.

@igorkudrin
igorkudrin merged commit d007100 into llvm:main Jul 18, 2026
11 checks passed
@igorkudrin
igorkudrin deleted the lldb-dap-tests-avoid-hang branch July 18, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants