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