summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp699
-rw-r--r--lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp162
-rw-r--r--lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h60
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);
OpenPOWER on IntegriCloud