OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/simulator.h" | 5 #include "vm/simulator.h" |
6 #include "vm/thread_interrupter.h" | 6 #include "vm/thread_interrupter.h" |
7 | 7 |
8 namespace dart { | 8 namespace dart { |
9 | 9 |
10 // Notes: | 10 // Notes: |
11 // | 11 // |
12 // The ThreadInterrupter interrupts all registered threads once per | 12 // The ThreadInterrupter interrupts all threads actively running isolates once |
13 // interrupt period (default is every millisecond). While the thread is | 13 // per interrupt period (default is 1 millisecond). While the thread is |
14 // interrupted, the thread's interrupt callback is invoked. Callbacks cannot | 14 // interrupted, the thread's interrupt callback is invoked. Callbacks cannot |
15 // rely on being executed on the interrupted thread. | 15 // rely on being executed on the interrupted thread. |
16 // | 16 // |
17 // There are two mechanisms used to interrupt a thread. The first, used on OSs | 17 // There are two mechanisms used to interrupt a thread. The first, used on OSs |
18 // with pthreads (Android, Linux, and Mac), is thread specific signal delivery. | 18 // with pthreads (Android, Linux, and Mac), is thread specific signal delivery. |
19 // The second, used on Windows, is explicit suspend and resume thread system | 19 // The second, used on Windows, is explicit suspend and resume thread system |
20 // calls. Signal delivery forbids taking locks and allocating memory (which | 20 // calls. Signal delivery forbids taking locks and allocating memory (which |
21 // takes a lock). Explicit suspend and resume means that the interrupt callback | 21 // takes a lock). Explicit suspend and resume means that the interrupt callback |
22 // will not be executing on the interrupted thread, making it meaningless to | 22 // will not be executing on the interrupted thread, making it meaningless to |
23 // access TLS from within the thread interrupt callback. Combining these | 23 // access TLS from within the thread interrupt callback. Combining these |
24 // limitations, thread interrupt callbacks are forbidden from: | 24 // limitations, thread interrupt callbacks are forbidden from: |
25 // | 25 // |
26 // * Accessing TLS. | 26 // * Accessing TLS. |
27 // * Allocating memory. | 27 // * Allocating memory. |
28 // * Taking a lock. | 28 // * Taking a lock. |
29 // | 29 // |
30 // The ThreadInterrupter has a single monitor (monitor_). This monitor guards | 30 // The ThreadInterrupter has a single monitor (monitor_). This monitor is used |
31 // access to the list of threads registered to receive interrupts (threads_). | 31 // to synchronize startup, shutdown, and waking up from a deep sleep. |
32 // | 32 // |
33 // A thread can only register and unregister itself. Each thread has a heap | 33 // A thread can only register and unregister itself. Each thread has a heap |
34 // allocated ThreadState. A thread's ThreadState is lazily allocated the first | 34 // allocated ThreadState. A thread's ThreadState is lazily allocated the first |
35 // time the thread is registered. A pointer to a thread's ThreadState is stored | 35 // time the thread is registered. A pointer to a thread's ThreadState is stored |
36 // in the list of threads registered to receive interrupts (threads_) and in | 36 // in the list of threads registered to receive interrupts (threads_) and in |
37 // thread local storage. When a thread's ThreadState is being modified, the | 37 // thread local storage. When a thread's ThreadState is being modified, the |
38 // thread local storage pointer is temporarily set to NULL while the | 38 // thread local storage pointer is temporarily set to NULL while the |
39 // modification is occurring. After the ThreadState has been updated, the | 39 // modification is occurring. After the ThreadState has been updated, the |
40 // thread local storage pointer is set again. This has an important side | 40 // thread local storage pointer is set again. This has an important side |
41 // effect: if the thread is interrupted by a signal handler during a ThreadState | 41 // effect: if the thread is interrupted by a signal handler during a ThreadState |
42 // update the signal handler will immediately return. | 42 // update the signal handler will immediately return. |
43 | 43 |
44 DEFINE_FLAG(bool, trace_thread_interrupter, false, | 44 DEFINE_FLAG(bool, trace_thread_interrupter, false, |
45 "Trace thread interrupter"); | 45 "Trace thread interrupter"); |
46 | 46 |
47 bool ThreadInterrupter::initialized_ = false; | 47 bool ThreadInterrupter::initialized_ = false; |
48 bool ThreadInterrupter::shutdown_ = false; | 48 bool ThreadInterrupter::shutdown_ = false; |
49 bool ThreadInterrupter::thread_running_ = false; | 49 bool ThreadInterrupter::thread_running_ = false; |
50 ThreadId ThreadInterrupter::interrupter_thread_id_ = Thread::kInvalidThreadId; | 50 ThreadId ThreadInterrupter::interrupter_thread_id_ = Thread::kInvalidThreadId; |
51 Monitor* ThreadInterrupter::monitor_ = NULL; | 51 Monitor* ThreadInterrupter::monitor_ = NULL; |
52 intptr_t ThreadInterrupter::interrupt_period_ = 1000; | 52 intptr_t ThreadInterrupter::interrupt_period_ = 1000; |
53 intptr_t ThreadInterrupter::current_wait_time_ = 0; | |
53 ThreadLocalKey ThreadInterrupter::thread_state_key_ = | 54 ThreadLocalKey ThreadInterrupter::thread_state_key_ = |
54 Thread::kUnsetThreadLocalKey; | 55 Thread::kUnsetThreadLocalKey; |
55 | 56 |
56 | 57 |
57 void ThreadInterrupter::InitOnce() { | 58 void ThreadInterrupter::InitOnce() { |
58 ASSERT(!initialized_); | 59 ASSERT(!initialized_); |
59 ASSERT(thread_state_key_ == Thread::kUnsetThreadLocalKey); | 60 ASSERT(thread_state_key_ == Thread::kUnsetThreadLocalKey); |
60 thread_state_key_ = Thread::CreateThreadLocal(); | 61 thread_state_key_ = Thread::CreateThreadLocal(); |
61 ASSERT(thread_state_key_ != Thread::kUnsetThreadLocalKey); | 62 ASSERT(thread_state_key_ != Thread::kUnsetThreadLocalKey); |
62 monitor_ = new Monitor(); | 63 monitor_ = new Monitor(); |
(...skipping 28 matching lines...) Expand all Loading... | |
91 if (shutdown_) { | 92 if (shutdown_) { |
92 // Already shutdown. | 93 // Already shutdown. |
93 return; | 94 return; |
94 } | 95 } |
95 shutdown_ = true; | 96 shutdown_ = true; |
96 ASSERT(initialized_); | 97 ASSERT(initialized_); |
97 if (FLAG_trace_thread_interrupter) { | 98 if (FLAG_trace_thread_interrupter) { |
98 OS::Print("ThreadInterrupter shutting down.\n"); | 99 OS::Print("ThreadInterrupter shutting down.\n"); |
99 } | 100 } |
100 while (thread_running_) { | 101 while (thread_running_) { |
102 // Notify in case of deep sleep. | |
103 shutdown_ml.Notify(); | |
101 shutdown_ml.Wait(); | 104 shutdown_ml.Wait(); |
Ivan Posva
2014/05/25 12:25:16
Aren't you waiting on your own notification in thi
Cutch
2014/05/26 06:59:35
Moved the Notify out of the loop and replaced it w
| |
102 } | 105 } |
103 // Join in the interrupter thread. On Windows, a thread's exit-code can | 106 // Join in the interrupter thread. On Windows, a thread's exit-code can |
104 // leak into the process's exit-code, if exiting 'at same time' as the | 107 // leak into the process's exit-code, if exiting 'at same time' as the |
105 // process ends. | 108 // process ends. |
106 if (interrupter_thread_id_ != Thread::kInvalidThreadId) { | 109 if (interrupter_thread_id_ != Thread::kInvalidThreadId) { |
107 Thread::Join(interrupter_thread_id_); | 110 Thread::Join(interrupter_thread_id_); |
108 interrupter_thread_id_ = Thread::kInvalidThreadId; | 111 interrupter_thread_id_ = Thread::kInvalidThreadId; |
109 } | 112 } |
110 } | 113 } |
111 if (FLAG_trace_thread_interrupter) { | 114 if (FLAG_trace_thread_interrupter) { |
112 OS::Print("ThreadInterrupter shut down.\n"); | 115 OS::Print("ThreadInterrupter shut down.\n"); |
113 } | 116 } |
114 } | 117 } |
115 | 118 |
116 // Delay between interrupts. | 119 // Delay between interrupts. |
117 void ThreadInterrupter::SetInterruptPeriod(intptr_t period) { | 120 void ThreadInterrupter::SetInterruptPeriod(intptr_t period) { |
118 if (shutdown_) { | 121 if (shutdown_) { |
119 return; | 122 return; |
120 } | 123 } |
121 ASSERT(initialized_); | 124 ASSERT(initialized_); |
122 ASSERT(period > 0); | 125 ASSERT(period > 0); |
123 interrupt_period_ = period; | 126 interrupt_period_ = period; |
124 } | 127 } |
125 | 128 |
126 | 129 |
130 void ThreadInterrupter::WakeUp() { | |
131 if (shutdown_) { | |
132 return; | |
133 } | |
134 ASSERT(initialized_); | |
135 { | |
136 MonitorLocker ml(monitor_); | |
137 if (!InDeepSleep()) { | |
138 // No need to notify, regularly waking up. | |
139 return; | |
140 } | |
141 // Notify the interrupter to wake it from its deep sleep. | |
142 ml.Notify(); | |
143 } | |
144 } | |
145 | |
127 // Register the currently running thread for interrupts. If the current thread | 146 // Register the currently running thread for interrupts. If the current thread |
128 // is already registered, callback and data will be updated. | 147 // is already registered, callback and data will be updated. |
129 InterruptableThreadState* ThreadInterrupter::Register( | 148 InterruptableThreadState* ThreadInterrupter::Register( |
130 ThreadInterruptCallback callback, void* data) { | 149 ThreadInterruptCallback callback, void* data) { |
131 if (shutdown_) { | 150 if (shutdown_) { |
132 return NULL; | 151 return NULL; |
133 } | 152 } |
134 ASSERT(initialized_); | 153 ASSERT(initialized_); |
135 InterruptableThreadState* state = _EnsureThreadStateCreated(); | 154 InterruptableThreadState* state = _EnsureThreadStateCreated(); |
136 // Set callback and data. | 155 // Set callback and data. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
213 } | 232 } |
214 | 233 |
215 | 234 |
216 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) { | 235 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) { |
217 // NoOp. | 236 // NoOp. |
218 } | 237 } |
219 | 238 |
220 | 239 |
221 class ThreadInterrupterVisitIsolates : public IsolateVisitor { | 240 class ThreadInterrupterVisitIsolates : public IsolateVisitor { |
222 public: | 241 public: |
223 ThreadInterrupterVisitIsolates() { } | 242 ThreadInterrupterVisitIsolates() { |
243 profiled_isolate_count_ = 0; | |
244 } | |
245 | |
224 void VisitIsolate(Isolate* isolate) { | 246 void VisitIsolate(Isolate* isolate) { |
225 ASSERT(isolate != NULL); | 247 ASSERT(isolate != NULL); |
226 isolate->ProfileInterrupt(); | 248 profiled_isolate_count_ += isolate->ProfileInterrupt(); |
Ivan Posva
2014/05/25 19:07:48
Please add a comment in ProfileInterrupt that the
| |
227 } | 249 } |
250 | |
251 intptr_t profiled_isolate_count() const { | |
252 return profiled_isolate_count_; | |
253 } | |
254 | |
255 void set_profiled_isolate_count(intptr_t profiled_isolate_count) { | |
256 profiled_isolate_count_ = profiled_isolate_count; | |
257 } | |
258 | |
259 private: | |
260 intptr_t profiled_isolate_count_; | |
228 }; | 261 }; |
229 | 262 |
230 | 263 |
231 void ThreadInterrupter::ThreadMain(uword parameters) { | 264 void ThreadInterrupter::ThreadMain(uword parameters) { |
232 ASSERT(initialized_); | 265 ASSERT(initialized_); |
233 InstallSignalHandler(); | 266 InstallSignalHandler(); |
234 if (FLAG_trace_thread_interrupter) { | 267 if (FLAG_trace_thread_interrupter) { |
235 OS::Print("ThreadInterrupter thread running.\n"); | 268 OS::Print("ThreadInterrupter thread running.\n"); |
236 } | 269 } |
237 { | 270 { |
238 // Signal to main thread we are ready. | 271 // Signal to main thread we are ready. |
239 MonitorLocker startup_ml(monitor_); | 272 MonitorLocker startup_ml(monitor_); |
240 interrupter_thread_id_ = Thread::GetCurrentThreadId(); | 273 interrupter_thread_id_ = Thread::GetCurrentThreadId(); |
241 thread_running_ = true; | 274 thread_running_ = true; |
242 startup_ml.Notify(); | 275 startup_ml.Notify(); |
243 } | 276 } |
244 { | 277 { |
278 ThreadInterrupterVisitIsolates visitor; | |
279 current_wait_time_ = interrupt_period_; | |
245 MonitorLocker wait_ml(monitor_); | 280 MonitorLocker wait_ml(monitor_); |
246 ThreadInterrupterVisitIsolates visitor; | |
247 while (!shutdown_) { | 281 while (!shutdown_) { |
282 intptr_t r = wait_ml.WaitMicros(current_wait_time_); | |
283 | |
284 if ((r == Monitor::kNotified) && InDeepSleep()) { | |
285 // Woken up from deep sleep. | |
286 ASSERT(visitor.profiled_isolate_count() == 0); | |
287 // Return to regular interrupts. | |
288 current_wait_time_ = interrupt_period_; | |
289 } | |
290 | |
291 // Reset count before visiting isolates. | |
292 visitor.set_profiled_isolate_count(0); | |
248 Isolate::VisitIsolates(&visitor); | 293 Isolate::VisitIsolates(&visitor); |
249 wait_ml.WaitMicros(interrupt_period_); | 294 |
295 if (visitor.profiled_isolate_count() == 0) { | |
296 // No isolates were profiled. In order to reduce unnecessary CPU | |
297 // load, we will wait until we are notified before attempting to | |
298 // interrupt again. | |
299 current_wait_time_ = 0; | |
Ivan Posva
2014/05/25 12:25:16
Please add a comment that a wait time of 0 means i
Cutch
2014/05/26 06:59:35
Switched to using Monitor::kNoTimeout which should
| |
300 continue; | |
301 } | |
302 | |
303 ASSERT(current_wait_time_ == interrupt_period_); | |
250 } | 304 } |
251 } | 305 } |
252 if (FLAG_trace_thread_interrupter) { | 306 if (FLAG_trace_thread_interrupter) { |
253 OS::Print("ThreadInterrupter thread exiting.\n"); | 307 OS::Print("ThreadInterrupter thread exiting.\n"); |
254 } | 308 } |
255 { | 309 { |
256 // Signal to main thread we are exiting. | 310 // Signal to main thread we are exiting. |
257 MonitorLocker shutdown_ml(monitor_); | 311 MonitorLocker shutdown_ml(monitor_); |
258 thread_running_ = false; | 312 thread_running_ = false; |
259 shutdown_ml.Notify(); | 313 shutdown_ml.Notify(); |
260 } | 314 } |
261 } | 315 } |
262 | 316 |
263 } // namespace dart | 317 } // namespace dart |
OLD | NEW |