1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
//===-- ThreadStateCoordinator.h --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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"
namespace lldb_private
{
class ThreadStateCoordinator
{
public:
// Typedefs.
typedef std::unordered_set<lldb::tid_t> ThreadIDSet;
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.
ThreadStateCoordinator (const LogFunction &log_function);
// 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,
const ErrorFunction &error_function);
// This method is the main purpose of the class: triggering a deferred
// action after all non-stopped threads stop. The triggering_tid is the
// thread id passed to the call_after_function. The error_function will
// be fired if the triggering tid is unknown at the time of execution.
void
CallAfterRunningThreadsStop (lldb::tid_t triggering_tid,
const ThreadIDFunction &request_thread_stop_function,
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
NotifyThreadStop (lldb::tid_t tid,
const ErrorFunction &error_function);
// 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.
//
// 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 ();
// 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. Always call this from the same thread. The processing
// logic assumes the execution of this is implicitly serialized.
EventLoopResult
ProcessNextEvent ();
private:
// Typedefs.
class EventBase;
class EventCallAfterThreadsStop;
class EventThreadStopped;
class EventThreadCreate;
class EventThreadDeath;
class EventRequestResume;
class EventStopCoordinator;
class EventReset;
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 ();
void
SetPendingNotification (const EventBaseSP &event_sp);
void
ThreadDidStop (lldb::tid_t tid, ErrorFunction &error_function);
void
ThreadWasCreated (lldb::tid_t tid, bool is_stopped, ErrorFunction &error_function);
void
ThreadDidDie (lldb::tid_t tid, ErrorFunction &error_function);
void
ResetNow ();
void
Log (const char *format, ...);
EventCallAfterThreadsStop *
GetPendingThreadStopNotification ();
// Member variables.
LogFunction m_log_function;
QueueType m_event_queue;
// For now we do simple read/write lock strategy with efficient wait-for-data.
// 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;
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;
};
}
#endif
|