Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(329)

Side by Side Diff: runtime/vm/thread_interrupter.cc

Issue 109803002: Profiler Take 2 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include <cstdio>
siva 2013/12/11 02:52:21 what is this? We don't include c++ header files n
Cutch 2013/12/11 17:44:56 Done.
6
7 #include "vm/thread_interrupter.h"
8
9 namespace dart {
10
11 // Notes:
12 //
13 // The ThreadInterrupter interrupts all registered threads once per
14 // interrupt period (default is every millisecond). While the thread is
15 // interrupted, the thread's interrupt callback is invoked. Callbacks cannot
16 // rely on being executed on the interrupted thread.
17 //
18 // There are two mechanisms used to interrupt a thread. The first, used on OSs
19 // with pthreads (Android, Linux, and Mac), is thread specific signal delivery.
20 // The second, used on Windows, is explicit suspend and resume thread system
21 // calls. Signal delivery forbids taking locks and allocating memory (which
22 // takes a lock). Explicit suspend and resume means that the interrupt callback
23 // will not be executing on the interrupted thread, making it meaningless to
24 // access TLS from within the thread interrupt callback. Combining these
25 // limitations, thread interrupt callbacks are forbidden from:
26 //
27 // * Accessing TLS.
28 // * Allocating memory.
29 // * Taking a lock.
30 //
31 // The ThreadInterrupter has a single monitor (monitor_). This monitor guards
32 // access to the list of threads registered to receive interrupts (threads_).
33 //
34 // A thread can only register and unregister itself. Each thread has a heap
35 // allocated ThreadState. A thread's ThreadState is lazily allocated the first
36 // time the thread is registered. A pointer to a thread's ThreadState is stored
37 // in the list of threads registered to receive interrupts (threads_) and in
38 // thread local storage. When a thread's ThreadState is being modified, the
39 // thread local storage pointer is temporarily set to NULL while the
40 // modification is occurring. After the ThreadState has been updated, the
41 // thread local storage pointer is set again. This has an important side
42 // effect: if the thread is interrupted by a signal handler during a ThreadState
43 // update the signal handler will immediately return.
44
45 DEFINE_FLAG(bool, thread_interrupter, true, "Enable thread interrupter");
46 DEFINE_FLAG(bool, trace_thread_interrupter, false,
47 "Trace thread interrupter");
48
49 bool ThreadInterrupter::initialized_ = false;
50 bool ThreadInterrupter::shutdown_ = false;
51 bool ThreadInterrupter::thread_running_ = false;
52 ThreadId ThreadInterrupter::interrupter_thread_id_ = Thread::kInvalidThreadId;
53 Monitor* ThreadInterrupter::monitor_ = NULL;
54 Monitor* ThreadInterrupter::start_stop_monitor_ = NULL;
55 intptr_t ThreadInterrupter::interrupt_period_ = 1000;
56 ThreadLocalKey ThreadInterrupter::thread_state_key_ =
57 Thread::kUnsetThreadLocalKey;
58 ThreadInterrupter::ThreadState** ThreadInterrupter::threads_ = NULL;
59 intptr_t ThreadInterrupter::threads_capacity_ = 0;
60 intptr_t ThreadInterrupter::threads_size_ = 0;
61
62
63 void ThreadInterrupter::InitOnce() {
64 if (!FLAG_thread_interrupter) {
65 return;
66 }
67 ASSERT(!initialized_);
68 initialized_ = true;
69 ASSERT(thread_state_key_ == Thread::kUnsetThreadLocalKey);
70 thread_state_key_ = Thread::CreateThreadLocal();
71 ASSERT(thread_state_key_ != Thread::kUnsetThreadLocalKey);
72 monitor_ = new Monitor();
73 start_stop_monitor_ = new Monitor();
74 ResizeThreads(16);
75 if (FLAG_trace_thread_interrupter) {
76 OS::Print("ThreadInterrupter starting up.\n");
77 }
78 ASSERT(interrupter_thread_id_ == Thread::kInvalidThreadId);
79 {
80 MonitorLocker startup_ml(start_stop_monitor_);
81 Thread::Start(ThreadMain, 0);
82 while (!thread_running_) {
83 startup_ml.Wait();
84 }
85 }
86 ASSERT(interrupter_thread_id_ != Thread::kInvalidThreadId);
87 if (FLAG_trace_thread_interrupter) {
88 OS::Print("ThreadInterrupter running.\n");
89 }
90 }
91
92
93 void ThreadInterrupter::Shutdown() {
94 if (!FLAG_thread_interrupter) {
95 return;
96 }
97 ASSERT(initialized_);
98 if (FLAG_trace_thread_interrupter) {
99 OS::Print("ThreadInterrupter shutting down.\n");
100 }
101 intptr_t size_at_shutdown = 0;
102 {
103 MonitorLocker ml(monitor_);
104 shutdown_ = true;
105 size_at_shutdown = threads_size_;
106 threads_size_ = 0;
107 threads_capacity_ = 0;
108 free(threads_);
109 threads_ = NULL;
110 ml.Notify();
111 }
112 {
113 MonitorLocker shutdown_ml(start_stop_monitor_);
114 while (thread_running_) {
115 shutdown_ml.Wait();
116 }
117 }
118 if (FLAG_trace_thread_interrupter) {
119 OS::Print("ThreadInterrupter shut down (%" Pd ").\n", size_at_shutdown);
120 }
121 }
122
123 // Delay between interrupts.
124 void ThreadInterrupter::SetInterruptPeriod(intptr_t period) {
125 if (!FLAG_thread_interrupter) {
126 return;
127 }
128 ASSERT(period > 0);
129 {
130 MonitorLocker ml(monitor_);
131 interrupt_period_ = period;
132 }
133 }
134
135
136 // Register the currently running thread for interrupts. If the current thread
137 // is already registered, callback and data will be updated.
138 void ThreadInterrupter::Register(ThreadInterruptCallback callback, void* data) {
139 if (!FLAG_thread_interrupter) {
140 return;
141 }
142 ThreadId current_thread = Thread::GetCurrentThreadId();
143 ASSERT(!Thread::Compare(current_thread, interrupter_thread_id_));
144 {
145 MonitorLocker ml(monitor_);
146 // Set callback and data.
147 UpdateStateObject(callback, data);
148
149 intptr_t i = FindThreadIndex(current_thread);
150 if (i >= 0) {
151 return;
152 }
153 AddThread(current_thread, callback, data);
154 if (FLAG_trace_thread_interrupter) {
155 intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
156 OS::Print("ThreadInterrupter Added %p\n", reinterpret_cast<void*>(tid));
157 }
158 ml.Notify();
siva 2013/12/11 02:52:21 Why is this notify needed? The next wake up of the
Cutch 2013/12/11 17:44:56 The notify is there in case we switch the thread t
159 }
160 }
161
162
163 // Unregister the currently running thread for interrupts.
164 void ThreadInterrupter::Unregister() {
165 if (!FLAG_thread_interrupter) {
166 return;
167 }
168 ThreadId current_thread = Thread::GetCurrentThreadId();
169 ASSERT(!Thread::Compare(current_thread, interrupter_thread_id_));
170 {
171 MonitorLocker ml(monitor_);
172 // Clear callback and data.
173 UpdateStateObject(NULL, NULL);
174
175 intptr_t index = FindThreadIndex(current_thread);
176 if (index < 0) {
177 // Not registered.
178 return;
179 }
180 ThreadState* state = RemoveThread(index);
181 ASSERT(state != NULL);
182 ASSERT(state == ThreadInterrupter::CurrentThreadState());
183 if (FLAG_trace_thread_interrupter) {
184 intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
185 OS::Print("ThreadInterrupter Removed %p\n", reinterpret_cast<void*>(tid));
186 }
187 ml.Notify();
siva 2013/12/11 02:52:21 Ditto comment about this Notify.
188 }
189 }
190
191
192 void ThreadInterrupter::UpdateStateObject(ThreadInterruptCallback callback,
193 void* data) {
194 ThreadState* state = CurrentThreadState();
195 ThreadId current_thread = Thread::GetCurrentThreadId();
196 if (state == NULL) {
197 // Create thread state object lazily.
198 if (FLAG_trace_thread_interrupter) {
199 intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
200 OS::Print("ThreadInterrupter Tracking %p\n",
201 reinterpret_cast<void*>(tid));
202 }
203 state = new ThreadState();
204 state->id = current_thread;
205 SetCurrentThreadState(state);
206 }
207 ASSERT(state != NULL);
208 ASSERT(Thread::Compare(state->id, Thread::GetCurrentThreadId()));
209 SetCurrentThreadState(NULL);
210 // It is now safe to modify the state object. If an interrupt occurs,
211 // the current thread state will be NULL.
212 state->callback = callback;
213 state->data = data;
214 SetCurrentThreadState(state);
215 if (FLAG_trace_thread_interrupter) {
216 intptr_t tid = Thread::ThreadIdToIntPtr(current_thread);
217 if (callback == NULL) {
218 OS::Print("ThreadInterrupter Cleared %p\n", reinterpret_cast<void*>(tid));
219 } else {
220 OS::Print("ThreadInterrupter Updated %p\n", reinterpret_cast<void*>(tid));
221 }
222 }
223 }
224
225
226 ThreadInterrupter::ThreadState* ThreadInterrupter::CurrentThreadState() {
227 return reinterpret_cast<ThreadState*>(
228 Thread::GetThreadLocal(thread_state_key_));
229 }
230
231
232 void ThreadInterrupter::SetCurrentThreadState(ThreadState* state) {
233 Thread::SetThreadLocal(thread_state_key_, reinterpret_cast<uword>(state));
234 }
235
236
237 void ThreadInterrupter::ResizeThreads(intptr_t new_capacity) {
siva 2013/12/11 02:52:21 // Must be called with monitor_ locked.
Cutch 2013/12/11 17:44:56 Done.
238 ASSERT(new_capacity < kMaxThreads);
239 ASSERT(new_capacity > threads_capacity_);
240 ThreadState* state = NULL;
241 threads_ = reinterpret_cast<ThreadState**>(
242 realloc(threads_, sizeof(state) * new_capacity));
243 for (intptr_t i = threads_capacity_; i < new_capacity; i++) {
244 threads_[i] = NULL;
245 }
246 threads_capacity_ = new_capacity;
247 }
248
249
250 void ThreadInterrupter::AddThread(ThreadId id, ThreadInterruptCallback callback,
251 void* data) {
252 // Must be called with monitor_ locked.
253 if (threads_ == NULL) {
254 // We are shutting down.
255 return;
256 }
257 if (threads_size_ == threads_capacity_) {
258 ResizeThreads(threads_capacity_ == 0 ? 16 : threads_capacity_ * 2);
259 }
260 UpdateStateObject(callback, data);
261 ThreadState* state = CurrentThreadState();
262 ASSERT(state != NULL);
263 threads_[threads_size_] = state;
264 threads_size_++;
265 }
266
267
268 intptr_t ThreadInterrupter::FindThreadIndex(ThreadId id) {
269 // Must be called with monitor_ locked.
270 if (threads_ == NULL) {
271 // We are shutting down.
272 return -1;
273 }
274 for (intptr_t i = 0; i < threads_size_; i++) {
275 if (threads_[i]->id == id) {
276 return i;
277 }
278 }
279 return -1;
280 }
281
282
283 ThreadInterrupter::ThreadState* ThreadInterrupter::RemoveThread(intptr_t i) {
284 // Must be called with monitor_ locked.
285 if (threads_ == NULL) {
286 // We are shutting down.
287 return NULL;
288 }
289 ASSERT(i < threads_size_);
290 ThreadState* state = threads_[i];
291 ASSERT(state != NULL);
292 intptr_t last = threads_size_ - 1;
293 if (i != last) {
294 threads_[i] = threads_[last];
295 }
296 // Mark last as NULL.
297 threads_[last] = NULL;
298 // Pop.
299 threads_size_--;
300 return state;
301 }
302
303
304 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) {
305 // NoOp.
306 }
307
308 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698