diff options
3 files changed, 541 insertions, 380 deletions
diff --git a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp index ac136e9c4ac..149cf65c2eb 100644 --- a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp @@ -22,460 +22,542 @@ namespace vprintf (format, args); printf ("\n"); } + + // These are single-line macros so that IDE integration of gtest results puts + // the error markers on the correct failure point within the gtest. + +#define ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \ +if (HasError ()) { printf ("unexpected error in processing of event, error: %s\n", m_error_string.c_str ()); } \ +ASSERT_EQ (false, HasError ()); + +#define ASSERT_PROCESS_NEXT_EVENT_FAILS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \ +ASSERT_EQ (true, HasError ()); + + class ThreadStateCoordinatorTest: public ::testing::Test + { + protected: + // Constants. + const lldb::tid_t TRIGGERING_TID = 4105; + const lldb::tid_t PENDING_STOP_TID = 3; + const lldb::tid_t PENDING_STOP_TID_02 = 29016; + const lldb::tid_t NEW_THREAD_TID = 1234; + + // Member variables. + bool m_error_called = false; + std::string m_error_string; + + ThreadStateCoordinator m_coordinator; + + bool m_deferred_notification_called; + lldb::tid_t m_deferred_notification_tid; + + ThreadStateCoordinator::ThreadIDSet m_requested_stop_tids; + + // Constructors. + ThreadStateCoordinatorTest () : + m_error_called (false), + m_error_string (), + m_coordinator (StdoutLogger), + m_deferred_notification_called (false), + m_deferred_notification_tid (0), + m_requested_stop_tids () + { + } + + // Member functions. + + // Error handling. + void + SetErrorString (const std::string &error_string) + { + m_error_called = true; + m_error_string = error_string; + printf ("received error: %s (test might be expecting)\n", error_string.c_str ()); + } + + ThreadStateCoordinator::ErrorFunction + GetErrorFunction () + { + using namespace std::placeholders; + return std::bind(&ThreadStateCoordinatorTest::SetErrorString, this, _1); + } + + bool + HasError () const + { + return m_error_called; + } + + // Deferred notificaton reception. + void + DeferredStopNotificationHandler (lldb::tid_t triggered_tid) + { + m_deferred_notification_called = true; + m_deferred_notification_tid = triggered_tid; + } + + ThreadStateCoordinator::ThreadIDFunction + GetDeferredStopNotificationFunction () + { + using namespace std::placeholders; + return std::bind(&ThreadStateCoordinatorTest::DeferredStopNotificationHandler, this, _1); + } + + bool + DidFireDeferredNotification () const + { + return m_deferred_notification_called; + } + + lldb::tid_t + GetDeferredNotificationTID () const + { + return m_deferred_notification_tid; + } + + // Stop request call reception. + void + ThreadStopRequested (lldb::tid_t stop_tid) + { + m_requested_stop_tids.insert (stop_tid); + } + + ThreadStateCoordinator::ThreadIDFunction + GetStopRequestFunction () + { + using namespace std::placeholders; + return std::bind(&ThreadStateCoordinatorTest::ThreadStopRequested, this, _1); + } + + ThreadStateCoordinator::ThreadIDSet::size_type + GetRequestedStopCount () const + { + return m_requested_stop_tids.size(); + } + + bool + DidRequestStopForTid (lldb::tid_t tid) + { + return m_requested_stop_tids.find (tid) != m_requested_stop_tids.end (); + } + + // Test state initialization helpers. + void + SetupKnownRunningThread (lldb::tid_t tid) + { + NotifyThreadCreate (tid, false); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); + } + + void + SetupKnownStoppedThread (lldb::tid_t tid) + { + NotifyThreadCreate (tid, true); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); + } + + // Convenience wrappers for ThreadStateCoordinator, using defaults for expected arguments + // that plug into the test case handlers. + void + CallAfterThreadsStop (lldb::tid_t deferred_tid, + const ThreadStateCoordinator::ThreadIDSet &pending_stop_wait_tids) + { + m_coordinator.CallAfterThreadsStop (deferred_tid, + pending_stop_wait_tids, + GetStopRequestFunction (), + GetDeferredStopNotificationFunction (), + GetErrorFunction ()); + } + + void + NotifyThreadCreate (lldb::tid_t stopped_tid, bool thread_is_stopped) + { + m_coordinator.NotifyThreadCreate (stopped_tid, thread_is_stopped, GetErrorFunction ()); + } + + void + NotifyThreadStop (lldb::tid_t stopped_tid) + { + m_coordinator.NotifyThreadStop (stopped_tid, GetErrorFunction ()); + } + + void + NotifyThreadDeath (lldb::tid_t tid) + { + m_coordinator.NotifyThreadDeath (tid, GetErrorFunction ()); + } +}; +} + +TEST_F (ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents) +{ + m_coordinator.StopCoordinator (); + ASSERT_EQ (ThreadStateCoordinator::eventLoopResultStop, m_coordinator.ProcessNextEvent ()); + ASSERT_EQ (false, HasError ()); +} + +TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread) +{ + const lldb::tid_t UNKNOWN_TID = 678; + + // Notify an unknown thread has stopped. + NotifyThreadStop (UNKNOWN_TID); + ASSERT_PROCESS_NEXT_EVENT_FAILS (); } -TEST(ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents) +TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread) { - ThreadStateCoordinator coordinator(NOPLogger); + const lldb::tid_t UNKNOWN_TRIGGER_TID = 678; + + // Defer notify for an unknown thread. + CallAfterThreadsStop (UNKNOWN_TRIGGER_TID, + EMPTY_THREAD_ID_SET); + + // Shouldn't have fired yet. + ASSERT_EQ (false, DidFireDeferredNotification ()); - coordinator.StopCoordinator (); + // Event should fail because trigger tid is unknown. + ASSERT_PROCESS_NEXT_EVENT_FAILS (); - ASSERT_EQ(false, coordinator.ProcessNextEvent ()); + // Shouldn't have fired due to error. + ASSERT_EQ (false, DidFireDeferredNotification ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops) +TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPendingStopThread) { - ThreadStateCoordinator coordinator(NOPLogger); + // Let the coordinator know about our thread. + SetupKnownStoppedThread (TRIGGERING_TID); + + // Defer notify for an unknown thread. + const lldb::tid_t UNKNOWN_PENDING_STOP_TID = 7890; + ThreadStateCoordinator::ThreadIDSet pending_stop_tids { UNKNOWN_PENDING_STOP_TID }; + + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); + + // Shouldn't have fired yet. + ASSERT_EQ (false, DidFireDeferredNotification ()); + + // Event should fail because trigger tid is unknown. + ASSERT_PROCESS_NEXT_EVENT_FAILS (); - const lldb::tid_t TRIGGERING_TID = 4105; - bool call_after_fired = false; - lldb::tid_t reported_firing_tid = 0; + // Shouldn't have triggered deferred notification due to error. + ASSERT_EQ (false, DidFireDeferredNotification ()); + + // Shouldn't have triggered stop request due to unknown tid. + ASSERT_EQ (0, GetRequestedStopCount ()); +} + +TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops) +{ + // Let the coordinator know about our thread. + SetupKnownStoppedThread (TRIGGERING_TID); // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped. - coordinator.CallAfterThreadsStop (TRIGGERING_TID, - EMPTY_THREAD_ID_SET, - [](lldb::tid_t tid) {}, - [&](lldb::tid_t tid) { - call_after_fired = true; - reported_firing_tid = tid; - }); + CallAfterThreadsStop (TRIGGERING_TID, + EMPTY_THREAD_ID_SET); // Notification trigger shouldn't go off yet. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Process next event. This will pick up the call after threads stop event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Now the trigger should have fired, since there were no threads that needed to first stop. - ASSERT_EQ (true, call_after_fired); - - // And the firing tid should be the one we indicated. - ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop) +TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop) { - ThreadStateCoordinator coordinator(NOPLogger); + // Let the coordinator know about our thread. + SetupKnownStoppedThread (TRIGGERING_TID); - const lldb::tid_t TRIGGERING_TID = 4105; - const lldb::tid_t PENDING_STOP_TID = 3; + // Let the coordinator know about a currently-running thread we'll wait on. + SetupKnownRunningThread (PENDING_STOP_TID); 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; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (false, request_thread_stop_called); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID)); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_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); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Now report the that the pending stop occurred. - coordinator.NotifyThreadStop (PENDING_STOP_TID); + NotifyThreadStop (PENDING_STOP_TID); // Shouldn't take effect until after next processing step. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Deferred signal notification should have fired now. - ASSERT_EQ (true, call_after_fired); - ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops) +TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops) { - 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; + // Setup threads. + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_STOP_TID); + SetupKnownRunningThread (PENDING_STOP_TID_02); ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 }; - bool call_after_fired = false; - lldb::tid_t reported_firing_tid = 0; - - int request_thread_stop_calls = 0; - ThreadStateCoordinator::ThreadIDSet request_thread_stop_tids; - // 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_tids.insert (tid); - - }, - [&](lldb::tid_t tid) { - call_after_fired = true; - reported_firing_tid = tid; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (0, request_thread_stop_calls); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (0, GetRequestedStopCount ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Now the request thread stops should have been called for the pending stop tids. - ASSERT_EQ (2, request_thread_stop_calls); - ASSERT_EQ (1, request_thread_stop_tids.count (PENDING_STOP_TID)); - ASSERT_EQ (1, request_thread_stop_tids.count (PENDING_STOP_TID_02)); + ASSERT_EQ (2, GetRequestedStopCount ()); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID)); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02)); // 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); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Report the that the first pending stop occurred. - coordinator.NotifyThreadStop (PENDING_STOP_TID); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + NotifyThreadStop (PENDING_STOP_TID); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Shouldn't take effect until after both pending threads are notified. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Report the that the first pending stop occurred. - coordinator.NotifyThreadStop (PENDING_STOP_TID_02); - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + NotifyThreadStop (PENDING_STOP_TID_02); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Deferred signal notification should have fired now. - ASSERT_EQ (true, call_after_fired); - ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped) +TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped) { - ThreadStateCoordinator coordinator(NOPLogger); - - const lldb::tid_t TRIGGERING_TID = 4105; - const lldb::tid_t PENDING_STOP_TID = 3; + // Setup threads. + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_STOP_TID); 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; + // Tell m_coordinator the pending stop tid is already stopped. + NotifyThreadStop (PENDING_STOP_TID); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (false, request_thread_stop_called); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID)); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); - // The pending stop should *not* fire because the coordinator knows it has already stopped. - ASSERT_EQ (false, request_thread_stop_called); + // The pending stop should *not* fire because the m_coordinator knows it has already stopped. + ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID)); // 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); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAlreadyStopped) +TEST_F (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; + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_STOP_TID); + SetupKnownRunningThread (PENDING_STOP_TID_02); 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; + NotifyThreadStop (PENDING_STOP_TID); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (0, request_thread_stop_calls); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (0, GetRequestedStopCount ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02)); + ASSERT_EQ (false, DidRequestStopForTid (PENDING_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); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Notify final thread has stopped. - coordinator.NotifyThreadStop (PENDING_STOP_TID_02); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + NotifyThreadStop (PENDING_STOP_TID_02); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies) +TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies) { - ThreadStateCoordinator coordinator(NOPLogger); - - const lldb::tid_t TRIGGERING_TID = 4105; - const lldb::tid_t PENDING_STOP_TID = 3; + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_STOP_TID); 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; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (false, request_thread_stop_called); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (0, GetRequestedStopCount ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // 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); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID)); // But we still shouldn't have the deferred signal call go off yet. Need to wait for the death to be reported. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Now report the that the thread with pending stop dies. - coordinator.NotifyThreadDeath (PENDING_STOP_TID); + NotifyThreadDeath (PENDING_STOP_TID); // Shouldn't take effect until after next processing step. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Deferred signal notification should have fired now. - ASSERT_EQ (true, call_after_fired); - ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewThread) +TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewThread) { - ThreadStateCoordinator coordinator(NOPLogger); - - const lldb::tid_t TRIGGERING_TID = 4105; - const lldb::tid_t PENDING_STOP_TID = 3; + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_STOP_TID); ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID }; - bool call_after_fired = false; - lldb::tid_t reported_firing_tid = 0; - - int request_thread_stop_calls = 0; - ThreadStateCoordinator::ThreadIDSet request_thread_stop_tids; - // 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_tids.insert (tid); - - }, - [&](lldb::tid_t tid) { - call_after_fired = true; - reported_firing_tid = tid; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (0, request_thread_stop_calls); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (0, GetRequestedStopCount ()); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Now the request thread stop should have been called for the pending stop. - ASSERT_EQ (1, request_thread_stop_calls); - ASSERT_EQ (true, request_thread_stop_tids.count (PENDING_STOP_TID)); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID)); // But we still shouldn't have the deferred signal call go off yet. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Indicate a new thread has just been created. - const lldb::tid_t NEW_THREAD_TID = 1234; - - coordinator.NotifyThreadCreate (NEW_THREAD_TID); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + SetupKnownRunningThread (NEW_THREAD_TID); // We should have just received a stop request for the new thread id. - ASSERT_EQ (2, request_thread_stop_calls); - ASSERT_EQ (true, request_thread_stop_tids.count (NEW_THREAD_TID)); + ASSERT_EQ (2, GetRequestedStopCount ()); + ASSERT_EQ (true, DidRequestStopForTid (NEW_THREAD_TID)); // Now report the original pending tid stopped. This should no longer // trigger the pending notification because we should now require the // new thread to stop too. - coordinator.NotifyThreadStop (PENDING_STOP_TID); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); - ASSERT_EQ (false, call_after_fired); + NotifyThreadStop (PENDING_STOP_TID); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Now notify the new thread stopped. - coordinator.NotifyThreadStop (NEW_THREAD_TID); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + NotifyThreadStop (NEW_THREAD_TID); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Deferred signal notification should have fired now. - ASSERT_EQ (true, call_after_fired); - ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } -TEST(ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec) +TEST_F (ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec) { - ThreadStateCoordinator coordinator(NOPLogger); - - const lldb::tid_t TRIGGERING_TID = 4105; - bool call_after_fired = false; - lldb::tid_t reported_firing_tid = 0; + SetupKnownStoppedThread (TRIGGERING_TID); // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped. - coordinator.CallAfterThreadsStop (TRIGGERING_TID, - EMPTY_THREAD_ID_SET, - [](lldb::tid_t tid) {}, - [&](lldb::tid_t tid) { - call_after_fired = true; - reported_firing_tid = tid; - }); + CallAfterThreadsStop (TRIGGERING_TID, + EMPTY_THREAD_ID_SET); // Notification trigger shouldn't go off yet. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Now indicate an exec occurred, which will invalidate all state about the process and threads. - coordinator.ResetForExec (); + m_coordinator.ResetForExec (); // Verify the deferred stop notification does *not* fire with the next // process. It will handle the reset and not the deferred signaling, which // should now be removed. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); - ASSERT_EQ (false, call_after_fired); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); + ASSERT_EQ (false, DidFireDeferredNotification ()); } -TEST(ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIsStopped) +TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIsStopped) { - ThreadStateCoordinator coordinator(NOPLogger); - // Initialize thread to be in stopped state. - const lldb::tid_t TEST_TID = 1234; - - coordinator.NotifyThreadStop (TEST_TID); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + SetupKnownStoppedThread (NEW_THREAD_TID); // Request a resume. lldb::tid_t resumed_tid = 0; int resume_call_count = 0; - coordinator.RequestThreadResume (TEST_TID, - [&](lldb::tid_t tid) - { - ++resume_call_count; - resumed_tid = tid; - }); - + m_coordinator.RequestThreadResume (NEW_THREAD_TID, + [&](lldb::tid_t tid) + { + ++resume_call_count; + resumed_tid = tid; + }, + GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); // Process next event. After that, the resume request call should have fired. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); ASSERT_EQ (1, resume_call_count); - ASSERT_EQ (TEST_TID, resumed_tid); + ASSERT_EQ (NEW_THREAD_TID, resumed_tid); } -TEST(ThreadStateCoordinatorTest, RequestThreadResumeIgnoresCallbackWhenThreadIsRunning) +TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeIgnoresCallbackWhenThreadIsRunning) { - ThreadStateCoordinator coordinator(StdoutLogger); - // This thread will be assumed running (i.e. unknown, assumed running until marked stopped.) const lldb::tid_t TEST_TID = 1234; @@ -483,24 +565,25 @@ TEST(ThreadStateCoordinatorTest, RequestThreadResumeIgnoresCallbackWhenThreadIsR lldb::tid_t resumed_tid = 0; int resume_call_count = 0; - coordinator.RequestThreadResume (TEST_TID, + m_coordinator.RequestThreadResume (TEST_TID, [&](lldb::tid_t tid) { ++resume_call_count; resumed_tid = tid; - }); + }, + GetErrorFunction ()); // Shouldn't be called yet. ASSERT_EQ (0, resume_call_count); // Process next event. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // The resume request should not have gone off because we think it is already running. ASSERT_EQ (0, resume_call_count); } -TEST(ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingStopNotification) +TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingStopNotification) { // We're going to test this scenario: // * Deferred notification waiting on two threads, A and B. A and B currently running. @@ -513,68 +596,56 @@ TEST(ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingS // that seems wrong and possibly buggy since for that to happen, we would have intentionally called // a resume after the stop. Instead, we'll just log and indicate this looks suspicous. We can revisit // that decision after we see if/when that happens. - - ThreadStateCoordinator coordinator(StdoutLogger); - - const lldb::tid_t TRIGGERING_TID = 4105; const lldb::tid_t PENDING_TID_A = 2; const lldb::tid_t PENDING_TID_B = 89; - ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_TID_A, PENDING_TID_B }; + SetupKnownStoppedThread (TRIGGERING_TID); + SetupKnownRunningThread (PENDING_TID_A); + SetupKnownRunningThread (PENDING_TID_B); - bool call_after_fired = false; - lldb::tid_t reported_firing_tid = 0; - - int request_thread_stop_calls = 0; - ThreadStateCoordinator::ThreadIDSet request_thread_stop_tids; + ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_TID_A, PENDING_TID_B }; // 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_tids.insert (tid); - - }, - [&](lldb::tid_t tid) { - call_after_fired = true; - reported_firing_tid = tid; - }); + CallAfterThreadsStop (TRIGGERING_TID, + pending_stop_tids); // Neither trigger should have gone off yet. - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (0, request_thread_stop_calls); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_EQ (0, GetRequestedStopCount ()); // Execute CallAfterThreadsStop. - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // Both TID A and TID B should have had stop requests made. - ASSERT_EQ (2, request_thread_stop_calls); - ASSERT_EQ (1, request_thread_stop_tids.count (PENDING_TID_A)); - ASSERT_EQ (1, request_thread_stop_tids.count (PENDING_TID_B)); + ASSERT_EQ (2, GetRequestedStopCount ()); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_A)); + ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_B)); // But we still shouldn't have the deferred signal call go off yet. - ASSERT_EQ (false, call_after_fired); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Report thread A stopped. - coordinator.NotifyThreadStop (PENDING_TID_A); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); - ASSERT_EQ (false, call_after_fired); + NotifyThreadStop (PENDING_TID_A); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); + ASSERT_EQ (false, DidFireDeferredNotification ()); // Now report thread A is resuming. Ensure the resume is called. bool resume_called = false; - coordinator.RequestThreadResume (PENDING_TID_A, [&](lldb::tid_t tid) { resume_called = true; } ); + m_coordinator.RequestThreadResume (PENDING_TID_A, + [&](lldb::tid_t tid) { resume_called = true; }, + GetErrorFunction ()); ASSERT_EQ (false, resume_called); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); ASSERT_EQ (true, resume_called); // Report thread B stopped. - coordinator.NotifyThreadStop (PENDING_TID_B); - ASSERT_EQ (false, call_after_fired); - ASSERT_EQ (true, coordinator.ProcessNextEvent ()); + NotifyThreadStop (PENDING_TID_B); + ASSERT_EQ (false, DidFireDeferredNotification ()); + ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS (); // After notifying thread b stopped, we now have thread a resumed but thread b stopped. // However, since thread a had stopped, we now have had both requirements stopped at some point. // For now we'll expect this will fire the pending deferred stop notification. - ASSERT_EQ (true, call_after_fired); + ASSERT_EQ (true, DidFireDeferredNotification ()); + ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ()); } diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp index 4f19b0722f5..8a011e75ebd 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -17,6 +17,7 @@ #include "ThreadStateCoordinator.h" #include <memory> #include <cstdarg> +#include <sstream> using namespace lldb_private; @@ -35,7 +36,7 @@ public: } // Return false if the coordinator should terminate running. - virtual bool + virtual EventLoopResult ProcessEvent (ThreadStateCoordinator &coordinator) = 0; }; @@ -49,10 +50,10 @@ public: { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { - return false; + return eventLoopResultStop; } }; @@ -64,13 +65,15 @@ public: EventCallAfterThreadsStop (lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, const ThreadIDFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function): + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function): EventBase (), m_triggering_tid (triggering_tid), m_wait_for_stop_tids (wait_for_stop_tids), m_original_wait_for_stop_tids (wait_for_stop_tids), m_request_thread_stop_function (request_thread_stop_function), - m_call_after_function (call_after_function) + m_call_after_function (call_after_function), + m_error_function (error_function) { } @@ -98,9 +101,21 @@ public: return m_original_wait_for_stop_tids; } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { + // Validate we know about the deferred trigger thread. + if (auto find_it = coordinator.m_tid_stop_map.find (m_triggering_tid) == coordinator.m_tid_stop_map.end ()) + { + // We don't know about this thread. This is an error condition. + std::ostringstream error_message; + error_message << "error: deferred notification tid " << m_triggering_tid << " is unknown"; + m_error_function (error_message.str ()); + + // We bail out here. + return eventLoopResultContinue; + } + // 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. @@ -108,10 +123,24 @@ public: ThreadIDSet sent_tids; for (auto tid : m_wait_for_stop_tids) { - // 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. + // Validate we know about all tids for which we must first receive a stop before + // triggering the deferred stop notification. auto find_it = coordinator.m_tid_stop_map.find (tid); - if ((find_it == coordinator.m_tid_stop_map.end ()) || !find_it->second) + if (find_it == coordinator.m_tid_stop_map.end ()) + { + // This is an error. We shouldn't be asking for waiting pids that aren't known. + // NOTE: we may be stripping out the specification of wait tids and handle this + // automatically, in which case this state can never occur. + std::ostringstream error_message; + error_message << "error: deferred notification for tid " << m_triggering_tid << " specified an unknown/untracked pending stop tid " << m_triggering_tid; + m_error_function (error_message.str ()); + + // Bail out here. + return eventLoopResultContinue; + } + + // If the pending stop thread is currently running, we need to send it a stop request. + if (!find_it->second) { m_request_thread_stop_function (tid); sent_tids.insert (tid); @@ -137,7 +166,7 @@ public: coordinator.SetPendingNotification (shared_from_this ()); } - return true; + return eventLoopResultContinue; } // Return true if still pending thread stops waiting; false if no more stops. @@ -184,6 +213,7 @@ private: const ThreadIDSet m_original_wait_for_stop_tids; ThreadIDFunction m_request_thread_stop_function; ThreadIDFunction m_call_after_function; + ErrorFunction m_error_function; }; //===----------------------------------------------------------------------===// @@ -196,11 +226,11 @@ public: { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { coordinator.ResetNow (); - return true; + return eventLoopResultContinue; } }; @@ -209,22 +239,25 @@ public: class ThreadStateCoordinator::EventThreadStopped : public ThreadStateCoordinator::EventBase { public: - EventThreadStopped (lldb::tid_t tid): + EventThreadStopped (lldb::tid_t tid, + const ErrorFunction &error_function): EventBase (), - m_tid (tid) + m_tid (tid), + m_error_function (error_function) { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { - coordinator.ThreadDidStop (m_tid); - return true; + coordinator.ThreadDidStop (m_tid, m_error_function); + return eventLoopResultContinue; } private: const lldb::tid_t m_tid; + ErrorFunction m_error_function; }; //===----------------------------------------------------------------------===// @@ -232,22 +265,28 @@ private: class ThreadStateCoordinator::EventThreadCreate : public ThreadStateCoordinator::EventBase { public: - EventThreadCreate (lldb::tid_t tid): + EventThreadCreate (lldb::tid_t tid, + bool is_stopped, + const ErrorFunction &error_function): EventBase (), - m_tid (tid) + m_tid (tid), + m_is_stopped (is_stopped), + m_error_function (error_function) { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { - coordinator.ThreadWasCreated (m_tid); - return true; + coordinator.ThreadWasCreated (m_tid, m_is_stopped); + return eventLoopResultContinue; } private: const lldb::tid_t m_tid; + const bool m_is_stopped; + ErrorFunction m_error_function; }; //===----------------------------------------------------------------------===// @@ -255,22 +294,25 @@ private: class ThreadStateCoordinator::EventThreadDeath : public ThreadStateCoordinator::EventBase { public: - EventThreadDeath (lldb::tid_t tid): + EventThreadDeath (lldb::tid_t tid, + const ErrorFunction &error_function): EventBase (), - m_tid (tid) + m_tid (tid), + m_error_function (error_function) { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { coordinator.ThreadDidDie (m_tid); - return true; + return eventLoopResultContinue; } private: const lldb::tid_t m_tid; + ErrorFunction m_error_function; }; //===----------------------------------------------------------------------===// @@ -278,14 +320,17 @@ private: class ThreadStateCoordinator::EventRequestResume : public ThreadStateCoordinator::EventBase { public: - EventRequestResume (lldb::tid_t tid, const ThreadIDFunction &request_thread_resume_function): + EventRequestResume (lldb::tid_t tid, + const ThreadIDFunction &request_thread_resume_function, + const ErrorFunction &error_function): EventBase (), m_tid (tid), - m_request_thread_resume_function (request_thread_resume_function) + m_request_thread_resume_function (request_thread_resume_function), + m_error_function (error_function) { } - bool + EventLoopResult ProcessEvent(ThreadStateCoordinator &coordinator) override { // Tell the thread to resume if we don't already think it is running. @@ -294,13 +339,13 @@ public: { // Skip the resume call - we think it is already running because we don't know anything about the thread. coordinator.Log ("EventRequestResume::%s skipping resume request because we don't know about tid %" PRIu64 " and we therefore assume it is running.", __FUNCTION__, m_tid); - return true; + return eventLoopResultContinue; } else if (!find_it->second) { // Skip the resume call - we have tracked it to be running. coordinator.Log ("EventRequestResume::%s skipping resume request because tid %" PRIu64 " is already running according to our state tracking.", __FUNCTION__, m_tid); - return true; + return eventLoopResultContinue; } // Before we do the resume below, first check if we have a pending @@ -333,13 +378,14 @@ public: // to reflect it is running after this completes. m_request_thread_resume_function (m_tid); - return true; + return eventLoopResultContinue; } private: const lldb::tid_t m_tid; ThreadIDFunction m_request_thread_resume_function; + ErrorFunction m_error_function; }; //===----------------------------------------------------------------------===// @@ -403,19 +449,32 @@ void ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, const ThreadIDFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function) + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function) { EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid, wait_for_stop_tids, request_thread_stop_function, - call_after_function))); + call_after_function, + error_function))); } void -ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid) +ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, ErrorFunction &error_function) { + // Ensure we know about the thread. + auto find_it = m_tid_stop_map.find (tid); + if (find_it == m_tid_stop_map.end ()) + { + // We don't know about this thread. This is an error condition. + std::ostringstream error_message; + error_message << "error: tid " << tid << " asked to stop but tid is unknown"; + error_function (error_message.str ()); + return; + } + // Update the global list of known thread states. This one is definitely stopped. - m_tid_stop_map[tid] = true; + find_it->second = true; // If we have a pending notification, remove this from the set. EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); @@ -431,14 +490,13 @@ ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid) } void -ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid) +ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid, bool is_stopped) { // Add the new thread to the stop map. - // We assume a created thread is not stopped. - m_tid_stop_map[tid] = false; + m_tid_stop_map[tid] = is_stopped; EventCallAfterThreadsStop *const call_after_event = GetPendingThreadStopNotification (); - if (call_after_event) + if (call_after_event && !is_stopped) { // Tell the pending notification that we need to wait // for this new thread to stop. @@ -492,27 +550,33 @@ ThreadStateCoordinator::Log (const char *format, ...) } void -ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid) +ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid, + const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventThreadStopped (tid))); + EnqueueEvent (EventBaseSP (new EventThreadStopped (tid, error_function))); } void -ThreadStateCoordinator::RequestThreadResume (lldb::tid_t tid, const ThreadIDFunction &request_thread_resume_function) +ThreadStateCoordinator::RequestThreadResume (lldb::tid_t tid, + const ThreadIDFunction &request_thread_resume_function, + const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function))); + EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function))); } void -ThreadStateCoordinator::NotifyThreadCreate (lldb::tid_t tid) +ThreadStateCoordinator::NotifyThreadCreate (lldb::tid_t tid, + bool is_stopped, + const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventThreadCreate (tid))); + EnqueueEvent (EventBaseSP (new EventThreadCreate (tid, is_stopped, error_function))); } void -ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid) +ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid, + const ErrorFunction &error_function) { - EnqueueEvent (EventBaseSP (new EventThreadDeath (tid))); + EnqueueEvent (EventBaseSP (new EventThreadDeath (tid, error_function))); } void @@ -541,7 +605,7 @@ ThreadStateCoordinator::StopCoordinator () EnqueueEvent (EventBaseSP (new EventStopCoordinator ())); } -bool +ThreadStateCoordinator::EventLoopResult ThreadStateCoordinator::ProcessNextEvent () { return DequeueEventWithWait()->ProcessEvent (*this); diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h index 8aa40e285fe..3d2ed946163 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -28,36 +28,62 @@ namespace lldb_private // Typedefs. typedef std::unordered_set<lldb::tid_t> ThreadIDSet; - // Protocols. - + enum EventLoopResult + { + eventLoopResultContinue, + eventLoopResultStop + }; // Callback/block definitions. typedef std::function<void (lldb::tid_t tid)> ThreadIDFunction; typedef std::function<void (const char *format, va_list args)> LogFunction; + typedef std::function<void (const std::string &error_message)> ErrorFunction; - // constructors + // Constructors. ThreadStateCoordinator (const LogFunction &log_function); - // The main purpose of the class: triggering an action after - // a given set of threads stop. + // Notify the coordinator when a thread is created and/or starting to be + // tracked. is_stopped should be true if the thread is currently stopped; + // otherwise, it should be set false if it is already running. Will + // call the error function if the thread id is already tracked. void - CallAfterThreadsStop (lldb::tid_t triggering_tid, - const ThreadIDSet &wait_for_stop_tids, - const ThreadIDFunction &request_thread_stop_function, - const ThreadIDFunction &call_after_function); + NotifyThreadCreate (lldb::tid_t tid, + bool is_stopped, + const ErrorFunction &error_function); - // Notifications called when various state changes occur. + // Notify the coordinator when a previously-existing thread should no + // longer be tracked. The error_function will trigger if the thread + // is not being tracked. void - NotifyThreadStop (lldb::tid_t tid); + NotifyThreadDeath (lldb::tid_t tid, + const ErrorFunction &error_function); + + // This method is the main purpose of the class: triggering a deferred + // action after a given set of threads stop. The triggering_tid is the + // thread id passed to the call_after_function. The error_function will + // be fired if either the triggering tid or any of the wait_for_stop_tids + // are unknown at the time the method is processed. void - RequestThreadResume (lldb::tid_t tid, const ThreadIDFunction &request_thread_resume_func); + CallAfterThreadsStop (lldb::tid_t triggering_tid, + const ThreadIDSet &wait_for_stop_tids, + const ThreadIDFunction &request_thread_stop_function, + const ThreadIDFunction &call_after_function, + const ErrorFunction &error_function); + // Notify the thread stopped. Will trigger error at time of execution if we + // already think it is stopped. void - NotifyThreadCreate (lldb::tid_t tid); + NotifyThreadStop (lldb::tid_t tid, + const ErrorFunction &error_function); + // Request that the given thread id should have the request_thread_resume_function + // called. Will trigger the error_function if the thread is thought to be running + // already at that point. void - NotifyThreadDeath (lldb::tid_t tid); + RequestThreadResume (lldb::tid_t tid, + const ThreadIDFunction &request_thread_resume_function, + const ErrorFunction &error_function); // Indicate the calling process did an exec and that the thread state // should be 100% cleared. @@ -76,7 +102,7 @@ namespace lldb_private // Expected usage is to run this in a separate thread until the function // returns false. Always call this from the same thread. The processing // logic assumes the execution of this is implicitly serialized. - bool + EventLoopResult ProcessNextEvent (); private: @@ -111,10 +137,10 @@ namespace lldb_private SetPendingNotification (const EventBaseSP &event_sp); void - ThreadDidStop (lldb::tid_t tid); + ThreadDidStop (lldb::tid_t tid, ErrorFunction &error_function); void - ThreadWasCreated (lldb::tid_t tid); + ThreadWasCreated (lldb::tid_t tid, bool is_stopped); void ThreadDidDie (lldb::tid_t tid); |