lldb-dap: Fix race condition in event threads creation (#194012)

Move the registration of the SBListener to before
the event threads (`ProgressEventThread` and `EventThread`) start

This prevents a race condition where a stop event
could be missed if it was sent immediately after thread creation, which
would lead to a deadlock. It is most likely to happen under heavy CPU
load with test that fails early like
TestDAP_commands::test_command_directive_abort_on_error_init_commands.

Relevant logs.
```sh
# Event thread deadlock.
0x00007348BC000BE0 Listener('lldb-dap.progress.listener')::GetEventInternal, timeout = 1000000 us, event_mask = 0
0x00005b72419d1640 Broadcaster("lldb-dap")::BroadcastEvent (event_sp = 0x5b7241eebb60 Event: broadcaster = 0x5b72418e0df0 (lldb-dap), type = 0x00000001, data = <NULL>, unique=false) hijack = 0x0000000000000000
0x00005B7241898440 Listener('lldb.Debugger')::GetEventInternal, timeout = 1000000 us, event_mask = 0
0x7348bc000be0     Listener::GetEventInternal() timed out for lldb-dap.progress.listener
0x00007348BC000BE0 Listener('lldb-dap.progress.listener')::GetEventInternal, timeout = 1000000 us, event_mask = 0
```
```sh
# Progress thread deadlock.
0x000057798AB6B440 Listener('lldb.Debugger')::GetEventInternal, timeout = <infinite>, event_mask = 0
0x000057798aca4640 Broadcaster("lldb-dap")::BroadcastEvent (event_sp = 0x57798b0b9d80 Event: broadcaster = 0x57798abb3df0 (lldb-dap), type = 0x00000001, data = <NULL>, unique=false) hijack = 0x0000000000000000
0x57798ab6b440     Listener('lldb.Debugger')::AddEvent (event_sp = {0x57798b0b9d80})
0x57798ab6b440     'lldb.Debugger' Listener::FindNextEventInternal(broadcaster=(nil), event_type_mask=0x00000000, remove=1) event 0x57798b0b9d80
0x000057798aca4640 Broadcaster("lldb-dap")::BroadcastEvent (event_sp = 0x57798b1be800 Event: broadcaster = 0x57798abb3df0 (lldb-dap), type = 0x00000002, data = <NULL>, unique=false) hijack = 0x0000000000000000
0x000078023C000BE0 Listener('lldb-dap.progress.listener')::GetEventInternal, timeout = <infinite>, event_mask = 0
```
This commit is contained in:
Ebuka Ezike
2026-04-26 20:49:54 +01:00
committed by GitHub
parent 4e6d01dd5b
commit a562a10f73
4 changed files with 32 additions and 17 deletions

View File

@@ -1283,7 +1283,18 @@ void DAP::StartEventThread() {
}
void DAP::StartProgressEventThread() {
progress_event_thread = std::thread(&DAP::ProgressEventThread, this);
lldb::SBListener listener("lldb-dap.progress.listener");
// Add the listener for the 'StopProgressThread' event before starting the
// progress thread to prevent a race condition. Under heavy load, a stop event
// could be sent immediately after the thread starts. If the listener isn't
// registered first, the event is missed, leading to a deadlock.
broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
debugger.GetBroadcaster().AddListener(
listener, lldb::SBDebugger::eBroadcastBitProgress |
lldb::SBDebugger::eBroadcastBitExternalProgress);
progress_event_thread =
std::thread(&DAP::ProgressEventThread, this, listener);
}
void DAP::StartEventThreads() {
@@ -1372,12 +1383,7 @@ llvm::Error DAP::InitializeDebugger() {
return llvm::Error::success();
}
void DAP::ProgressEventThread() {
lldb::SBListener listener("lldb-dap.progress.listener");
debugger.GetBroadcaster().AddListener(
listener, lldb::SBDebugger::eBroadcastBitProgress |
lldb::SBDebugger::eBroadcastBitExternalProgress);
broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
void DAP::ProgressEventThread(lldb::SBListener listener) {
lldb::SBEvent event;
bool done = false;
while (!done) {
@@ -1430,6 +1436,7 @@ void DAP::ProgressEventThread() {
}
}
}
DAP_LOG(log, "Stopped ProgressEvent Thread.");
}
std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(

View File

@@ -477,7 +477,7 @@ private:
/// Event threads.
/// @{
void ProgressEventThread();
void ProgressEventThread(lldb::SBListener listener);
/// Event thread is a shared pointer in case we have a multiple
/// DAP instances sharing the same event thread.

View File

@@ -10,6 +10,7 @@
#include "EventHelper.h"
#include "lldb/API/SBBroadcaster.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBTarget.h"
#include "lldb/Host/MainLoopBase.h"
#include "llvm/Support/Threading.h"
@@ -105,6 +106,20 @@ DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
m_debugger_event_threads.erase(it);
}
// Add the listener for the 'StopEventThread' event before starting the
// event thread to prevent a race condition. Under heavy load, a stop event
// could be sent immediately after the thread starts. If the listener isn't
// registered first, the event is missed, leading to a deadlock.
lldb::SBListener listener = debugger.GetListener();
requesting_dap->broadcaster.AddListener(listener,
eBroadcastBitStopEventThread);
debugger.GetBroadcaster().AddListener(
listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
// listen for thread events.
listener.StartListeningForEventClass(
debugger, lldb::SBThread::GetBroadcasterClassName(),
lldb::SBThread::eBroadcastBitStackChanged);
// Create a new event thread and store it.
auto new_thread_sp = std::make_shared<ManagedEventThread>(
requesting_dap->broadcaster,

View File

@@ -658,17 +658,9 @@ void EventThread(lldb::SBDebugger debugger, lldb::SBBroadcaster broadcaster,
llvm::set_thread_name(thread_name);
lldb::SBListener listener = debugger.GetListener();
broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
debugger.GetBroadcaster().AddListener(
listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
// listen for thread events.
listener.StartListeningForEventClass(
debugger, lldb::SBThread::GetBroadcasterClassName(),
lldb::SBThread::eBroadcastBitStackChanged);
lldb::SBEvent event;
bool done = false;
while (!done) {
if (!listener.WaitForEvent(UINT32_MAX, event))
continue;
@@ -691,6 +683,7 @@ void EventThread(lldb::SBDebugger debugger, lldb::SBBroadcaster broadcaster,
}
}
}
DAP_LOG(log, "Stopped Event Thread.");
}
} // namespace lldb_dap