diff options
3 files changed, 196 insertions, 10 deletions
diff --git a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp index f83d99de402..b08c2c2e3a8 100644 --- a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp @@ -220,3 +220,118 @@ TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStop ASSERT_EQ (true, call_after_fired); ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); } + +TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAlreadyStopped) +{ + ThreadStateCoordinator coordinator(NOPLogger); + + const lldb::tid_t TRIGGERING_TID = 4105; + const lldb::tid_t PENDING_STOP_TID = 3; + const lldb::tid_t PENDING_STOP_TID_02 = 29016; + + ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 }; + + // 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; + + int request_thread_stop_calls = 0; + 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_calls; + 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 (0, request_thread_stop_calls); + + // Process next event. + ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + + // The pending stop should only fire for one of the threads, the one that wasn't already stopped. + ASSERT_EQ (1, request_thread_stop_calls); + ASSERT_EQ (PENDING_STOP_TID_02, request_thread_stop_tid); + + // The deferred signal notification should not yet have fired since all pending thread stops have not yet occurred. + ASSERT_EQ (false, call_after_fired); + + // Notify final thread has stopped. + coordinator.NotifyThreadStop (PENDING_STOP_TID_02); + ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + + // 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); +} + +TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies) +{ + 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 }; + + 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 ()); + + // Now the request thread stop should have been called for the pending stop. + ASSERT_EQ (true, request_thread_stop_called); + ASSERT_EQ (PENDING_STOP_TID, request_thread_stop_tid); + + // But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported. + ASSERT_EQ (false, call_after_fired); + + // Now report the that the thread with pending stop dies. + coordinator.NotifyThreadDeath (PENDING_STOP_TID); + + // Shouldn't take effect until after next processing step. + ASSERT_EQ (false, call_after_fired); + + // Process next event. + ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + + // Deferred signal notification should have fired now. + 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 8dfe03b1d6b..354dcc2bcc6 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -133,14 +133,34 @@ public: return true; } + // Return true if still pending thread stops waiting; false if no more stops. + // If no more pending stops, signal. + bool + RemoveThreadStopRequirementAndMaybeSignal (lldb::tid_t tid) + { + // Remove this tid if it was in it. + m_wait_for_stop_tids.erase (tid); + + // Fire pending notification if no pending thread stops remain. + if (m_wait_for_stop_tids.empty ()) + { + // Fire the pending notification now. + NotifyNow (); + return false; + } + + // Still have pending thread stops. + return true; + } + +private: + void NotifyNow () { m_call_after_func (m_triggering_tid); } -private: - const lldb::tid_t m_triggering_tid; ThreadIDSet m_wait_for_stop_tids; ThreadIDFunc m_request_thread_stop_func; @@ -176,6 +196,33 @@ private: //===----------------------------------------------------------------------===// +class ThreadStateCoordinator::EventThreadDeath : public ThreadStateCoordinator::EventBase +{ +public: + EventThreadDeath (lldb::tid_t tid): + EventBase (), + m_tid (tid) + { + } + + ~EventThreadDeath () override + { + } + + bool + ProcessEvent(ThreadStateCoordinator &coordinator) override + { + coordinator.ThreadDidDie (m_tid); + return true; + } + +private: + + const lldb::tid_t m_tid; +}; + +//===----------------------------------------------------------------------===// + ThreadStateCoordinator::ThreadStateCoordinator (const LogFunc &log_func) : m_log_func (log_func), m_event_queue (), @@ -253,17 +300,31 @@ ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid) // If we have a pending notification, remove this from the set. if (m_pending_notification_sp) { - EventCallAfterThreadsStop *call_after_event = static_cast<EventCallAfterThreadsStop*> (m_pending_notification_sp.get ()); + EventCallAfterThreadsStop *const call_after_event = static_cast<EventCallAfterThreadsStop*> (m_pending_notification_sp.get ()); + const bool pending_stops_remain = call_after_event->RemoveThreadStopRequirementAndMaybeSignal (tid); + if (!pending_stops_remain) + { + // Clear the pending notification now. + m_pending_notification_sp.reset (); + } + } +} - ThreadIDSet &remaining_stop_tids = call_after_event->GetRemainingWaitTIDs (); +void +ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid) +{ + // Update the global list of known thread states. While this one is stopped, it is also dead. + // So stop tracking it. We assume the user of this coordinator will not keep trying to add + // dependencies on a thread after it is known to be dead. + m_tid_stop_map.erase (tid); - // Remove this tid if it was in it. - remaining_stop_tids.erase (tid); - if (remaining_stop_tids.empty ()) + // If we have a pending notification, remove this from the set. + if (m_pending_notification_sp) + { + EventCallAfterThreadsStop *const call_after_event = static_cast<EventCallAfterThreadsStop*> (m_pending_notification_sp.get ()); + const bool pending_stops_remain = call_after_event->RemoveThreadStopRequirementAndMaybeSignal (tid); + if (!pending_stops_remain) { - // Fire the pending notification now. - call_after_event->NotifyNow (); - // Clear the pending notification now. m_pending_notification_sp.reset (); } @@ -288,6 +349,12 @@ ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid) } void +ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid) +{ + EnqueueEvent (EventBaseSP (new EventThreadDeath (tid))); +} + +void ThreadStateCoordinator::StopCoordinator () { EnqueueEvent (EventBaseSP (new EventStopCoordinator ())); diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h index be1b70a33ea..6fecb1ce90a 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -79,6 +79,7 @@ namespace lldb_private class EventCallAfterThreadsStop; class EventStopCoordinator; class EventThreadStopped; + class EventThreadDeath; typedef std::shared_ptr<EventBase> EventBaseSP; @@ -101,6 +102,9 @@ namespace lldb_private ThreadDidStop (lldb::tid_t tid); void + ThreadDidDie (lldb::tid_t tid); + + void Log (const char *format, ...); // Member variables. |

