diff options
3 files changed, 96 insertions, 22 deletions
diff --git a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp index 6835b110d73..ddf82cc711a 100644 --- a/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ b/lldb/gtest/unittest/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp @@ -403,3 +403,33 @@ TEST(ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewT ASSERT_EQ (TRIGGERING_TID, reported_firing_tid); } +TEST(ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec) +{ + ThreadStateCoordinator coordinator(NOPLogger); + + const lldb::tid_t TRIGGERING_TID = 4105; + bool call_after_fired = false; + lldb::tid_t reported_firing_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, + EMPTY_THREAD_ID_SET, + [](lldb::tid_t tid) {}, + [&](lldb::tid_t tid) { + call_after_fired = true; + reported_firing_tid = tid; + }); + + // Notification trigger shouldn't go off yet. + ASSERT_EQ (false, call_after_fired); + + // Now indicate an exec occurred, which will invalidate all state about the process and threads. + 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); +} + diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp index 78b9abbd88a..b6d1382577d 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -49,10 +49,6 @@ public: { } - ~EventStopCoordinator () override - { - } - bool ProcessEvent(ThreadStateCoordinator &coordinator) override { @@ -77,10 +73,6 @@ public: { } - ~EventCallAfterThreadsStop () override - { - } - lldb::tid_t GetTriggeringTID () const { return m_triggering_tid; @@ -181,6 +173,24 @@ private: //===----------------------------------------------------------------------===// +class ThreadStateCoordinator::EventReset : public ThreadStateCoordinator::EventBase +{ +public: + EventReset (): + EventBase () + { + } + + bool + ProcessEvent(ThreadStateCoordinator &coordinator) override + { + coordinator.ResetNow (); + return true; + } +}; + +//===----------------------------------------------------------------------===// + class ThreadStateCoordinator::EventThreadStopped : public ThreadStateCoordinator::EventBase { public: @@ -190,10 +200,6 @@ public: { } - ~EventThreadStopped () override - { - } - bool ProcessEvent(ThreadStateCoordinator &coordinator) override { @@ -217,10 +223,6 @@ public: { } - ~EventThreadCreate () override - { - } - bool ProcessEvent(ThreadStateCoordinator &coordinator) override { @@ -244,10 +246,6 @@ public: { } - ~EventThreadDeath () override - { - } - bool ProcessEvent(ThreadStateCoordinator &coordinator) override { @@ -387,6 +385,19 @@ ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid) } void +ThreadStateCoordinator::ResetNow () +{ + // Clear the pending notification if there was one. + m_pending_notification_sp.reset (); + + // Clear the stop map - we no longer know anything about any thread state. + // The caller is expected to reset thread states for all threads, and we + // will assume anything we haven't heard about is running and requires a + // stop. + m_tid_stop_map.clear (); +} + +void ThreadStateCoordinator::Log (const char *format, ...) { va_list args; @@ -416,6 +427,26 @@ ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid) } void +ThreadStateCoordinator::ResetForExec () +{ + std::lock_guard<std::mutex> lock (m_queue_mutex); + + // Remove everything from the queue. This is the only + // state mutation that takes place outside the processing + // loop. + QueueType empty_queue; + m_event_queue.swap (empty_queue); + + // Do the real clear behavior on the the queue to eliminate + // the chance that processing of a dequeued earlier event is + // overlapping with the clearing of state here. Push it + // directly because we need to have this happen with the lock, + // and so far I only have this one place that needs a no-lock + // variant. + m_event_queue.push (EventBaseSP (new EventReset ())); +} + +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 14399abfe08..f27dc67099f 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -59,6 +59,14 @@ namespace lldb_private void NotifyThreadDeath (lldb::tid_t tid); + // Indicate the calling process did an exec and that the thread state + // should be 100% cleared. + // + // Note this will clear out any pending notifications, but will not stop + // a notification currently in progress via ProcessNextEvent(). + void + ResetForExec (); + // Indicate when the coordinator should shut down. void StopCoordinator (); @@ -77,11 +85,13 @@ namespace lldb_private class EventBase; class EventCallAfterThreadsStop; - class EventStopCoordinator; class EventThreadStopped; class EventThreadCreate; class EventThreadDeath; + class EventStopCoordinator; + class EventReset; + typedef std::shared_ptr<EventBase> EventBaseSP; typedef std::queue<EventBaseSP> QueueType; @@ -109,6 +119,9 @@ namespace lldb_private ThreadDidDie (lldb::tid_t tid); void + ResetNow (); + + void Log (const char *format, ...); // Member variables. @@ -116,7 +129,7 @@ namespace lldb_private QueueType m_event_queue; // For now we do simple read/write lock strategy with efficient wait-for-data. - // We can replace with entirely non-blocking queue later but we still want the + // We can replace with an entirely non-blocking queue later but we still want the // reader to sleep when nothing is available - this will be a bursty but infrequent // event mechanism. std::condition_variable m_queue_condition; |

