summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins
diff options
context:
space:
mode:
authorTodd Fiala <todd.fiala@gmail.com>2014-09-26 19:08:00 +0000
committerTodd Fiala <todd.fiala@gmail.com>2014-09-26 19:08:00 +0000
commit80bef312b54d50ed23d3ae96968aa5e9d2fac242 (patch)
tree367f4a965875232d6661cdc925169d85cd94ae84 /lldb/source/Plugins
parent2dd3129b0ac365f356f6873b6fbe322171d65465 (diff)
downloadbcm5719-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.cpp255
-rw-r--r--lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h63
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;
};
}
OpenPOWER on IntegriCloud