summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen McMorran <bemcmorr@microsoft.com>2023-06-29 16:10:46 (GMT)
committerBrad King <brad.king@kitware.com>2023-06-29 17:25:51 (GMT)
commit764258771afb92067dd4b2c044d8d19abc6f932c (patch)
tree9f54476173cf01208690ded4d9cc6f3ccad729fb
parent7952e114668e97c207a6fc210184765f052d033b (diff)
downloadCMake-764258771afb92067dd4b2c044d8d19abc6f932c.zip
CMake-764258771afb92067dd4b2c044d8d19abc6f932c.tar.gz
CMake-764258771afb92067dd4b2c044d8d19abc6f932c.tar.bz2
Debugger: Fix threads request segfault after thread exited event
Fixes: #25041
-rw-r--r--Source/cmDebuggerAdapter.cxx14
-rw-r--r--Tests/CMakeLib/testDebuggerAdapter.cxx30
2 files changed, 39 insertions, 5 deletions
diff --git a/Source/cmDebuggerAdapter.cxx b/Source/cmDebuggerAdapter.cxx
index d03f79d..c2e0d4f 100644
--- a/Source/cmDebuggerAdapter.cxx
+++ b/Source/cmDebuggerAdapter.cxx
@@ -167,10 +167,16 @@ cmDebuggerAdapter::cmDebuggerAdapter(
(void)req;
std::unique_lock<std::mutex> lock(Mutex);
dap::ThreadsResponse response;
- dap::Thread thread;
- thread.id = DefaultThread->GetId();
- thread.name = DefaultThread->GetName();
- response.threads.push_back(thread);
+
+ // If a client requests threads during shutdown (like after receiving the
+ // thread exited event), DefaultThread won't be set.
+ if (DefaultThread) {
+ dap::Thread thread;
+ thread.id = DefaultThread->GetId();
+ thread.name = DefaultThread->GetName();
+ response.threads.push_back(thread);
+ }
+
return response;
});
diff --git a/Tests/CMakeLib/testDebuggerAdapter.cxx b/Tests/CMakeLib/testDebuggerAdapter.cxx
index 394986b..e66d990 100644
--- a/Tests/CMakeLib/testDebuggerAdapter.cxx
+++ b/Tests/CMakeLib/testDebuggerAdapter.cxx
@@ -53,7 +53,7 @@ public:
std::shared_ptr<dap::ReaderWriter> DebuggerToClient;
};
-bool testBasicProtocol()
+bool runTest(std::function<bool(dap::Session&)> onThreadExitedEvent)
{
std::promise<bool> debuggerAdapterInitializedPromise;
std::future<bool> debuggerAdapterInitializedFuture =
@@ -152,6 +152,11 @@ bool testBasicProtocol()
std::future_status::ready);
ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) ==
std::future_status::ready);
+
+ if (onThreadExitedEvent) {
+ ASSERT_TRUE(onThreadExitedEvent(*client));
+ }
+
ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) ==
std::future_status::ready);
ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) ==
@@ -165,9 +170,32 @@ bool testBasicProtocol()
return true;
}
+bool testBasicProtocol()
+{
+ return runTest(nullptr);
+}
+
+bool testThreadsRequestAfterThreadExitedEvent()
+{
+ return runTest([](dap::Session& session) -> bool {
+ // Try requesting threads again after receiving the thread exited event.
+ // Some clients do this to ensure that their thread list is up-to-date.
+ dap::ThreadsRequest threadsRequest;
+ auto threadsResponse = session.send(threadsRequest).get();
+ ASSERT_TRUE(!threadsResponse.error);
+
+ // CMake only has one DAP thread. Once that thread exits, there should be
+ // no threads left.
+ ASSERT_TRUE(threadsResponse.response.threads.empty());
+
+ return true;
+ });
+}
+
int testDebuggerAdapter(int, char*[])
{
return runTests(std::vector<std::function<bool()>>{
testBasicProtocol,
+ testThreadsRequestAfterThreadExitedEvent,
});
}