diff --git a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp index 1964aec84c8b..765a0ce861d0 100644 --- a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp @@ -110,3 +110,52 @@ TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop) ASSERT_EQ (true, call_after_fired); ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); } + +TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped) +{ + ThreadStateCoordinator coordinator(NOPLogger); + + const lldb::tid_t TRIGGERING_TID = 4105; + const lldb::tid_t PENDING_STOP_TID = 3; + + ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID }; + + // Tell coordinator the pending stop tid is already stopped. + coordinator.NotifyThreadStop (PENDING_STOP_TID); + ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + + // Now fire the deferred thread stop notification, indicating that the pending thread + // must be stopped before we notify. + bool call_after_fired = false; + lldb::tid_t reported_firing_tid = 0; + + bool request_thread_stop_called = false; + lldb::tid_t request_thread_stop_tid = 0; + + // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped. + coordinator.CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids, + [&](lldb::tid_t tid) { + request_thread_stop_called = true; + request_thread_stop_tid = tid; + + }, + [&](lldb::tid_t tid) { + call_after_fired = true; + reported_firing_tid = tid; + }); + + // Neither trigger should have gone off yet. + ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, request_thread_stop_called); + + // Process next event. + ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + + // The pending stop should *not* fire because the coordinator knows it has already stopped. + ASSERT_EQ (false, request_thread_stop_called); + + // The deferred signal notification should have fired since all requirements were met. + ASSERT_EQ (true, call_after_fired); + ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); +} diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp index 50f8b2e9b6b5..22f2f592951a 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -94,12 +94,27 @@ public: bool ProcessEvent(ThreadStateCoordinator &coordinator) override { - // Request a stop for all the thread stops that are needed. - // In the future, we can keep track of which stops we're - // still waiting for, and can not re-issue for already - // requested stops. + // Request a stop for all the thread stops that need to be stopped + // and are not already known to be stopped. Keep a list of all the + // threads from which we still need to hear a stop reply. + + ThreadIDSet sent_tids; for (auto tid : m_wait_for_stop_tids) - m_request_thread_stop_func (tid); + { + // If we don't know about the thread's stop state or we + // know it is not stopped, we need to send it a stop request. + auto find_it = coordinator.m_tid_stop_map.find (tid); + if ((find_it == coordinator.m_tid_stop_map.end ()) || !find_it->second) + { + m_request_thread_stop_func (tid); + sent_tids.insert (tid); + } + } + + // We only need to wait for the sent_tids - so swap our wait set + // to the sent tids. The rest are already stopped and we won't + // be receiving stop notifications for them. + m_wait_for_stop_tids.swap (sent_tids); if (m_wait_for_stop_tids.empty ()) {