Skip to content

Commit 871f483

Browse files
authored
[lldb-dap] Fix a race during shutdown (#91591)
lldb-dap was setting a flag which was meant to shut it down as soon as it sent a terminated event. The problem with this flag is two-fold: - as far as I can tell (definitely not an expert here), there's no justification for this in the protocol spec. The only way I found to shut the server down was to send it a disconnect request. - the flag did not actually work most of the time, because it's only checked between requests so nothing will happen if the server starts listening for a new request before a different thread manages to send the terminated event. And since the next request is usually the disconnect request, everything will operate normally. The combination of these two things meant that the issue was largely unnoticable, except for rare flaky test failures, which happened when the handler thread was too slow, and checked the flag after it has already been said. This caused the test suite to complain as it did not get a response to the disconnect request. This situation could be s(t)imulated by adding a sleep to the and of the main loop, which delayed the flag check, and caused the DAP tests to fail reliably. This patch changes the shutdown condition to only trigger when the disconnect request has been received. Since the flag can now only be set from the handler thread, it no longer needs to be atomic.
1 parent 062f6fe commit 871f483

File tree

3 files changed

+10
-22
lines changed

3 files changed

+10
-22
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ DAP::DAP()
3939
{"objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC},
4040
{"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
4141
{"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
42-
focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
43-
stop_at_entry(false), is_attach(false),
42+
focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
4443
enable_auto_variable_summaries(false),
4544
enable_synthetic_child_debugging(false),
4645
restarting_process_id(LLDB_INVALID_PROCESS_ID),
@@ -623,7 +622,7 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
623622
}
624623

625624
llvm::Error DAP::Loop() {
626-
while (!sent_terminated_event) {
625+
while (!disconnecting) {
627626
llvm::json::Object object;
628627
lldb_dap::PacketStatus status = GetNextObject(object);
629628

lldb/tools/lldb-dap/DAP.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ struct DAP {
168168
// arguments if we get a RestartRequest.
169169
std::optional<llvm::json::Object> last_launch_or_attach_request;
170170
lldb::tid_t focus_tid;
171-
std::atomic<bool> sent_terminated_event;
171+
bool disconnecting = false;
172172
bool stop_at_entry;
173173
bool is_attach;
174174
bool enable_auto_variable_summaries;

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -226,26 +226,14 @@ void SendContinuedEvent() {
226226
// Send a "terminated" event to indicate the process is done being
227227
// debugged.
228228
void SendTerminatedEvent() {
229-
// If an inferior exits prior to the processing of a disconnect request, then
230-
// the threads executing EventThreadFunction and request_discontinue
231-
// respectively may call SendTerminatedEvent simultaneously. Without any
232-
// synchronization, the thread executing EventThreadFunction may set
233-
// g_dap.sent_terminated_event before the thread executing
234-
// request_discontinue has had a chance to test it, in which case the latter
235-
// would move ahead to issue a response to the disconnect request. Said
236-
// response may get dispatched ahead of the terminated event compelling the
237-
// client to terminate the debug session without consuming any console output
238-
// that might've been generated by the execution of terminateCommands. So,
239-
// synchronize simultaneous calls to SendTerminatedEvent.
229+
// Prevent races if the process exits while we're being asked to disconnect.
240230
static std::mutex mutex;
241231
std::lock_guard<std::mutex> locker(mutex);
242-
if (!g_dap.sent_terminated_event) {
243-
g_dap.sent_terminated_event = true;
244-
g_dap.RunTerminateCommands();
245-
// Send a "terminated" event
246-
llvm::json::Object event(CreateTerminatedEventObject());
247-
g_dap.SendJSON(llvm::json::Value(std::move(event)));
248-
}
232+
233+
g_dap.RunTerminateCommands();
234+
// Send a "terminated" event
235+
llvm::json::Object event(CreateTerminatedEventObject());
236+
g_dap.SendJSON(llvm::json::Value(std::move(event)));
249237
}
250238

251239
// Send a thread stopped event for all threads as long as the process
@@ -1003,6 +991,7 @@ void request_disconnect(const llvm::json::Object &request) {
1003991
g_dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
1004992
g_dap.progress_event_thread.join();
1005993
}
994+
g_dap.disconnecting = true;
1006995
}
1007996

1008997
void request_exceptionInfo(const llvm::json::Object &request) {

0 commit comments

Comments
 (0)