[Lldb-commits] [lldb] r218833 - thread state coordinator: added error callbacks, cleaned up tests.

Todd Fiala todd.fiala at gmail.com
Wed Oct 1 14:40:45 PDT 2014


Author: tfiala
Date: Wed Oct  1 16:40:45 2014
New Revision: 218833

URL: http://llvm.org/viewvc/llvm-project?rev=218833&view=rev
Log:
thread state coordinator: added error callbacks, cleaned up tests.

ThreadStateCoordinator changes:
* Most commands that run in the queue now take an error handler that
  will be called with an error string if an error occurs during processing.
  Errors generally stop the operation in progress.  The errors are checked
  at time of execution.  This is intended to help flush out ptrace/waitpid/state management
  issues as quickly as possible.

* Threads now must be known to the coordinator before stops can be reported,
  resumes can be requested, thread deaths can be reported, or deferred stop
  notifications can be made.  Failure to know the thread will cause the coordinator
  to call the error callback for the event being processed.  Threads are introduced
  to the system by the NotifyThreadCreate method.
  
* The NotifyThreadCreate method now takes the initial state of the thread being
  introduces to the system.  We no longer just assume the thread is running.
  
The test cases were cleaned up, too:
* A gtest test fixture is now used, which allows creating less verbose helper
  methods that setup common pieces of callback code for some method invocations.
  Net result: the tests are simpler to read and shorter to write.

Modified:
    lldb/trunk/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp
    lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp
    lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.h

Modified: lldb/trunk/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp?rev=218833&r1=218832&r2=218833&view=diff
==============================================================================
--- lldb/trunk/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp (original)
+++ lldb/trunk/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp Wed Oct  1 16:40:45 2014
@@ -22,460 +22,542 @@ namespace
         vprintf (format, args);
         printf ("\n");
     }
-}
 
-TEST(ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents)
-{
-    ThreadStateCoordinator coordinator(NOPLogger);
+    // These are single-line macros so that IDE integration of gtest results puts
+    // the error markers on the correct failure point within the gtest.
 
-    coordinator.StopCoordinator ();
+#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_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread)
+{
+    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 ());
+
+    // Event should fail because trigger tid is unknown.
+    ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+
+    // Shouldn't have fired due to error.
+    ASSERT_EQ (false, DidFireDeferredNotification ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPendingStopThread)
+{
+    // 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 ();
+
+    // Shouldn't have triggered deferred notification due to error.
+    ASSERT_EQ (false, DidFireDeferredNotification ());
 
-    ASSERT_EQ(false, coordinator.ProcessNextEvent ());
+    // Shouldn't have triggered stop request due to unknown tid.
+    ASSERT_EQ (0, GetRequestedStopCount ());
 }
 
-TEST(ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
 {
-    ThreadStateCoordinator coordinator(NOPLogger);
-
-    const lldb::tid_t TRIGGERING_TID = 4105;
-    bool call_after_fired = false;
-    lldb::tid_t reported_firing_tid = 0;
+    // 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, Request
     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, Resumed
     //   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 };
-
-    bool call_after_fired = false;
-    lldb::tid_t reported_firing_tid = 0;
+    SetupKnownStoppedThread (TRIGGERING_TID);
+    SetupKnownRunningThread (PENDING_TID_A);
+    SetupKnownRunningThread (PENDING_TID_B);
 
-    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 ());
 }

Modified: lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp?rev=218833&r1=218832&r2=218833&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp Wed Oct  1 16:40:45 2014
@@ -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 (l
 }
 
 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
 }
 
 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);

Modified: lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.h?rev=218833&r1=218832&r2=218833&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.h (original)
+++ lldb/trunk/source/Plugins/Process/Linux/ThreadStateCoordinator.h Wed Oct  1 16:40:45 2014
@@ -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
+        NotifyThreadCreate (lldb::tid_t tid,
+                            bool is_stopped,
+                            const ErrorFunction &error_function);
+
+        // 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
+        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
         CallAfterThreadsStop (lldb::tid_t triggering_tid,
                               const ThreadIDSet &wait_for_stop_tids,
                               const ThreadIDFunction &request_thread_stop_function,
-                              const ThreadIDFunction &call_after_function);
-
-        // Notifications called when various state changes occur.
-        void
-        NotifyThreadStop (lldb::tid_t tid);
+                              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
-        RequestThreadResume (lldb::tid_t tid, const ThreadIDFunction &request_thread_resume_func);
+        NotifyThreadStop (lldb::tid_t tid,
+                          const ErrorFunction &error_function);
 
-        void
-        NotifyThreadCreate (lldb::tid_t tid);
-
-        void
-        NotifyThreadDeath (lldb::tid_t tid);
+        // 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
+        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);





More information about the lldb-commits mailing list