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:
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user