diff options
| author | Todd Fiala <todd.fiala@gmail.com> | 2014-09-26 19:08:00 +0000 |
|---|---|---|
| committer | Todd Fiala <todd.fiala@gmail.com> | 2014-09-26 19:08:00 +0000 |
| commit | 80bef312b54d50ed23d3ae96968aa5e9d2fac242 (patch) | |
| tree | 367f4a965875232d6661cdc925169d85cd94ae84 /lldb/source/Plugins | |
| parent | 2dd3129b0ac365f356f6873b6fbe322171d65465 (diff) | |
| download | bcm5719-llvm-80bef312b54d50ed23d3ae96968aa5e9d2fac242.tar.gz bcm5719-llvm-80bef312b54d50ed23d3ae96968aa5e9d2fac242.zip | |
gtest: tweaked test runner to fix an extra comma, added more tdd-based thread coordinator behavior.
Starting to flesh out the thread state coordinator class that will be used
by Linux/llgs.
llvm-svn: 218537
Diffstat (limited to 'lldb/source/Plugins')
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp | 255 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h | 63 |
2 files changed, 302 insertions, 16 deletions
diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp index 40bcebe6847..50f8b2e9b6b 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp @@ -7,14 +7,214 @@ // //===----------------------------------------------------------------------===// + +#if !defined (__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS 1 +#endif + +#include <inttypes.h> + #include "ThreadStateCoordinator.h" +#include <memory> using namespace lldb_private; +//===----------------------------------------------------------------------===// + +class ThreadStateCoordinator::EventBase : public std::enable_shared_from_this<ThreadStateCoordinator::EventBase> +{ +public: + EventBase () + { + } + + virtual + ~EventBase () + { + } + + // Return false if the coordinator should terminate running. + virtual bool + ProcessEvent (ThreadStateCoordinator &coordinator) = 0; +}; + +//===----------------------------------------------------------------------===// + +class ThreadStateCoordinator::EventStopCoordinator : public ThreadStateCoordinator::EventBase +{ +public: + EventStopCoordinator (): + EventBase () + { + } + + ~EventStopCoordinator () override + { + } + + bool + ProcessEvent(ThreadStateCoordinator &coordinator) override + { + return false; + } +}; + +//===----------------------------------------------------------------------===// + +class ThreadStateCoordinator::EventCallAfterThreadsStop : public ThreadStateCoordinator::EventBase +{ +public: + EventCallAfterThreadsStop (lldb::tid_t triggering_tid, + const ThreadIDSet &wait_for_stop_tids, + const ThreadIDFunc &request_thread_stop_func, + const ThreadIDFunc &call_after_func): + EventBase (), + m_triggering_tid (triggering_tid), + m_wait_for_stop_tids (wait_for_stop_tids), + m_request_thread_stop_func (request_thread_stop_func), + m_call_after_func (call_after_func) + { + } + + ~EventCallAfterThreadsStop () override + { + } + + lldb::tid_t GetTriggeringTID () const + { + return m_triggering_tid; + } + + ThreadIDSet & + GetRemainingWaitTIDs () + { + return m_wait_for_stop_tids; + } + + bool + ProcessEvent(ThreadStateCoordinator &coordinator) override + { + // Request a stop for all the thread stops that are needed. + // In the future, we can keep track of which stops we're + // still waiting for, and can not re-issue for already + // requested stops. + for (auto tid : m_wait_for_stop_tids) + m_request_thread_stop_func (tid); + + if (m_wait_for_stop_tids.empty ()) + { + // We're not waiting for any threads. Fire off the deferred signal delivery event. + NotifyNow (); + } + else + { + // The real meat of this class: wait until all + // the thread stops (or thread deaths) come in + // before firing off that the triggering signal + // arrived. + coordinator.SetPendingNotification (shared_from_this ()); + } + + return true; + } + + 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; + ThreadIDFunc m_call_after_func; +}; + +//===----------------------------------------------------------------------===// + +class ThreadStateCoordinator::EventThreadStopped : public ThreadStateCoordinator::EventBase +{ +public: + EventThreadStopped (lldb::tid_t tid): + EventBase (), + m_tid (tid) + { + } + + ~EventThreadStopped () override + { + } + + bool + ProcessEvent(ThreadStateCoordinator &coordinator) override + { + coordinator.ThreadDidStop (m_tid); + return true; + } + +private: + + const lldb::tid_t m_tid; +}; + +//===----------------------------------------------------------------------===// + ThreadStateCoordinator::ThreadStateCoordinator (const LogFunc &log_func) : - m_done_b (false), - m_log_func (log_func) + m_log_func (log_func), + m_event_queue (), + m_queue_condition (), + m_queue_mutex (), + m_tid_stop_map () +{ +} + +void +ThreadStateCoordinator::EnqueueEvent (EventBaseSP event_sp) +{ + std::lock_guard<std::mutex> lock (m_queue_mutex); + m_event_queue.push (event_sp); + m_queue_condition.notify_one (); +} + +ThreadStateCoordinator::EventBaseSP +ThreadStateCoordinator::DequeueEventWithWait () +{ + // Wait for an event to be present. + std::unique_lock<std::mutex> lock (m_queue_mutex); + m_queue_condition.wait (lock, + [this] { return !m_event_queue.empty (); }); + + // Grab the event and pop it off the queue. + EventBaseSP event_sp = m_event_queue.front (); + m_event_queue.pop (); + + return event_sp; +} + +void +ThreadStateCoordinator::SetPendingNotification (const EventBaseSP &event_sp) { + assert (event_sp && "null event_sp"); + if (!event_sp) + return; + + const EventCallAfterThreadsStop *new_call_after_event = static_cast<EventCallAfterThreadsStop*> (event_sp.get ()); + + if (m_pending_notification_sp) + { + const EventCallAfterThreadsStop *prev_call_after_event = static_cast<EventCallAfterThreadsStop*> (m_pending_notification_sp.get ()); + + // Yikes - we've already got a pending signal notification in progress. + // Log this info. We lose the pending notification here. + Log ("ThreadStateCoordinator::%s dropping existing pending signal notification for tid %" PRIu64 ", to be replaced with signal for tid %" PRIu64, + __FUNCTION__, + prev_call_after_event->GetTriggeringTID (), + new_call_after_event->GetTriggeringTID ()); + } + + m_pending_notification_sp = event_sp; } void @@ -23,16 +223,63 @@ ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid, const ThreadIDFunc &request_thread_stop_func, const ThreadIDFunc &call_after_func) { + EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid, + wait_for_stop_tids, + request_thread_stop_func, + call_after_func))); +} + +void +ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid) +{ + // Update the global list of known thread states. This one is definitely stopped. + m_tid_stop_map[tid] = true; + + // 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 ()); + + auto remaining_stop_tids = call_after_event->GetRemainingWaitTIDs (); + + // Remove this tid if it was in it. + remaining_stop_tids.erase (tid); + if (remaining_stop_tids.empty ()) + { + // Fire the pending notification now. + call_after_event->NotifyNow (); + + // Clear the pending notification now. + m_pending_notification_sp.reset (); + } + } +} + +void +ThreadStateCoordinator::Log (const char *format, ...) +{ + va_list args; + va_start (args, format); + + m_log_func (format, args); + + va_end (args); +} + +void +ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid) +{ + EnqueueEvent (EventBaseSP (new EventThreadStopped (tid))); } void ThreadStateCoordinator::StopCoordinator () { - m_done_b = true; + EnqueueEvent (EventBaseSP (new EventStopCoordinator ())); } bool ThreadStateCoordinator::ProcessNextEvent () { - return !m_done_b; + return DequeueEventWithWait()->ProcessEvent (*this); } diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h index a275ce651a3..be1b70a33ea 100644 --- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h @@ -10,7 +10,11 @@ #ifndef lldb_ThreadStateCoordinator_h #define lldb_ThreadStateCoordinator_h +#include <condition_variable> #include <functional> +#include <mutex> +#include <queue> +#include <unordered_map> #include <unordered_set> #include "lldb/lldb-types.h" @@ -24,6 +28,9 @@ namespace lldb_private // Typedefs. typedef std::unordered_set<lldb::tid_t> ThreadIDSet; + // Protocols. + + // Callback definitions. typedef std::function<void (lldb::tid_t tid)> ThreadIDFunc; typedef std::function<void (const char *format, va_list args)> LogFunc; @@ -34,7 +41,7 @@ namespace lldb_private // The main purpose of the class: triggering an action after // a given set of threads stop. void - CallAfterThreadsStop (const lldb::tid_t triggering_tid, + CallAfterThreadsStop (lldb::tid_t triggering_tid, const ThreadIDSet &wait_for_stop_tids, const ThreadIDFunc &request_thread_stop_func, const ThreadIDFunc &call_after_func); @@ -59,26 +66,58 @@ namespace lldb_private // Process the next event, returning false when the coordinator is all done. // This call is synchronous and blocks when there are no events pending. // Expected usage is to run this in a separate thread until the function - // returns false. + // returns false. Always call this from the same thread. The processing + // logic assumes the execution of this is implicitly serialized. bool ProcessNextEvent (); private: - enum EventType - { - eInvalid, - eEventTypeCallAfterThreadsStop, - eEventTypeThreadStopped, - eEventTypeThreadResumed, - eEventTypeThreadCreated, - eEventTypeThreadDied, - }; + // Typedefs. + class EventBase; + + class EventCallAfterThreadsStop; + class EventStopCoordinator; + class EventThreadStopped; + + typedef std::shared_ptr<EventBase> EventBaseSP; + + typedef std::queue<EventBaseSP> QueueType; + + typedef std::unordered_map<lldb::tid_t, bool> TIDBoolMap; + + + // Private member functions. + void + EnqueueEvent (EventBaseSP event_sp); + + EventBaseSP + DequeueEventWithWait (); - bool m_done_b; + void + SetPendingNotification (const EventBaseSP &event_sp); + + void + ThreadDidStop (lldb::tid_t tid); + void + Log (const char *format, ...); + + // Member variables. LogFunc m_log_func; + 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 + // reader to sleep when nothing is available - this will be a bursty but infrequent + // event mechanism. + std::condition_variable m_queue_condition; + std::mutex m_queue_mutex; + + EventBaseSP m_pending_notification_sp; + + // Maps known TIDs to stop (true) or not-stopped (false) state. + TIDBoolMap m_tid_stop_map; }; } |

