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_ = Monitor::kNoTimeout; |
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 23 matching lines...) Expand all Loading... |
86 | 87 |
87 | 88 |
88 void ThreadInterrupter::Shutdown() { | 89 void ThreadInterrupter::Shutdown() { |
89 { | 90 { |
90 MonitorLocker shutdown_ml(monitor_); | 91 MonitorLocker shutdown_ml(monitor_); |
91 if (shutdown_) { | 92 if (shutdown_) { |
92 // Already shutdown. | 93 // Already shutdown. |
93 return; | 94 return; |
94 } | 95 } |
95 shutdown_ = true; | 96 shutdown_ = true; |
| 97 // Notify. |
| 98 monitor_->Notify(); |
96 ASSERT(initialized_); | 99 ASSERT(initialized_); |
97 if (FLAG_trace_thread_interrupter) { | 100 if (FLAG_trace_thread_interrupter) { |
98 OS::Print("ThreadInterrupter shutting down.\n"); | 101 OS::Print("ThreadInterrupter shutting down.\n"); |
99 } | 102 } |
| 103 } |
| 104 #if defined(TARGET_OS_WINDOWS) |
| 105 // On Windows, a thread's exit-code can leak into the process's exit-code, |
| 106 // if exiting 'at same time' as the process ends. By joining with the thread |
| 107 // here, we avoid this race condition. |
| 108 ASSERT(interrupter_thread_id_ != Thread::kInvalidThreadId); |
| 109 Thread::Join(interrupter_thread_id_); |
| 110 interrupter_thread_id_ = Thread::kInvalidThreadId; |
| 111 #else |
| 112 // On non-Windows platforms, just wait for the thread interrupter to signal |
| 113 // that it has exited the loop. |
| 114 { |
| 115 MonitorLocker shutdown_ml(monitor_); |
100 while (thread_running_) { | 116 while (thread_running_) { |
| 117 // Wait for thread to exit. |
101 shutdown_ml.Wait(); | 118 shutdown_ml.Wait(); |
102 } | 119 } |
103 // 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 | |
105 // process ends. | |
106 if (interrupter_thread_id_ != Thread::kInvalidThreadId) { | |
107 Thread::Join(interrupter_thread_id_); | |
108 interrupter_thread_id_ = Thread::kInvalidThreadId; | |
109 } | |
110 } | 120 } |
| 121 #endif |
111 if (FLAG_trace_thread_interrupter) { | 122 if (FLAG_trace_thread_interrupter) { |
112 OS::Print("ThreadInterrupter shut down.\n"); | 123 OS::Print("ThreadInterrupter shut down.\n"); |
113 } | 124 } |
114 } | 125 } |
115 | 126 |
116 // Delay between interrupts. | 127 // Delay between interrupts. |
117 void ThreadInterrupter::SetInterruptPeriod(intptr_t period) { | 128 void ThreadInterrupter::SetInterruptPeriod(intptr_t period) { |
118 if (shutdown_) { | 129 if (shutdown_) { |
119 return; | 130 return; |
120 } | 131 } |
121 ASSERT(initialized_); | 132 ASSERT(initialized_); |
122 ASSERT(period > 0); | 133 ASSERT(period > 0); |
123 interrupt_period_ = period; | 134 interrupt_period_ = period; |
124 } | 135 } |
125 | 136 |
126 | 137 |
| 138 void ThreadInterrupter::WakeUp() { |
| 139 ASSERT(initialized_); |
| 140 { |
| 141 MonitorLocker ml(monitor_); |
| 142 if (!InDeepSleep()) { |
| 143 // No need to notify, regularly waking up. |
| 144 return; |
| 145 } |
| 146 // Notify the interrupter to wake it from its deep sleep. |
| 147 ml.Notify(); |
| 148 } |
| 149 } |
| 150 |
127 // Register the currently running thread for interrupts. If the current thread | 151 // Register the currently running thread for interrupts. If the current thread |
128 // is already registered, callback and data will be updated. | 152 // is already registered, callback and data will be updated. |
129 InterruptableThreadState* ThreadInterrupter::Register( | 153 InterruptableThreadState* ThreadInterrupter::Register( |
130 ThreadInterruptCallback callback, void* data) { | 154 ThreadInterruptCallback callback, void* data) { |
131 if (shutdown_) { | 155 if (shutdown_) { |
132 return NULL; | 156 return NULL; |
133 } | 157 } |
134 ASSERT(initialized_); | 158 ASSERT(initialized_); |
135 InterruptableThreadState* state = _EnsureThreadStateCreated(); | 159 InterruptableThreadState* state = _EnsureThreadStateCreated(); |
136 // Set callback and data. | 160 // Set callback and data. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 } | 237 } |
214 | 238 |
215 | 239 |
216 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) { | 240 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) { |
217 // NoOp. | 241 // NoOp. |
218 } | 242 } |
219 | 243 |
220 | 244 |
221 class ThreadInterrupterVisitIsolates : public IsolateVisitor { | 245 class ThreadInterrupterVisitIsolates : public IsolateVisitor { |
222 public: | 246 public: |
223 ThreadInterrupterVisitIsolates() { } | 247 ThreadInterrupterVisitIsolates() { |
| 248 profiled_thread_count_ = 0; |
| 249 } |
| 250 |
224 void VisitIsolate(Isolate* isolate) { | 251 void VisitIsolate(Isolate* isolate) { |
225 ASSERT(isolate != NULL); | 252 ASSERT(isolate != NULL); |
226 isolate->ProfileInterrupt(); | 253 profiled_thread_count_ += isolate->ProfileInterrupt(); |
227 } | 254 } |
| 255 |
| 256 intptr_t profiled_thread_count() const { |
| 257 return profiled_thread_count_; |
| 258 } |
| 259 |
| 260 void set_profiled_thread_count(intptr_t profiled_thread_count) { |
| 261 profiled_thread_count_ = profiled_thread_count; |
| 262 } |
| 263 |
| 264 private: |
| 265 intptr_t profiled_thread_count_; |
228 }; | 266 }; |
229 | 267 |
230 | 268 |
231 void ThreadInterrupter::ThreadMain(uword parameters) { | 269 void ThreadInterrupter::ThreadMain(uword parameters) { |
232 ASSERT(initialized_); | 270 ASSERT(initialized_); |
233 InstallSignalHandler(); | 271 InstallSignalHandler(); |
234 if (FLAG_trace_thread_interrupter) { | 272 if (FLAG_trace_thread_interrupter) { |
235 OS::Print("ThreadInterrupter thread running.\n"); | 273 OS::Print("ThreadInterrupter thread running.\n"); |
236 } | 274 } |
237 { | 275 { |
238 // Signal to main thread we are ready. | 276 // Signal to main thread we are ready. |
239 MonitorLocker startup_ml(monitor_); | 277 MonitorLocker startup_ml(monitor_); |
240 interrupter_thread_id_ = Thread::GetCurrentThreadId(); | 278 interrupter_thread_id_ = Thread::GetCurrentThreadId(); |
241 thread_running_ = true; | 279 thread_running_ = true; |
242 startup_ml.Notify(); | 280 startup_ml.Notify(); |
243 } | 281 } |
244 { | 282 { |
| 283 ThreadInterrupterVisitIsolates visitor; |
| 284 current_wait_time_ = interrupt_period_; |
245 MonitorLocker wait_ml(monitor_); | 285 MonitorLocker wait_ml(monitor_); |
246 ThreadInterrupterVisitIsolates visitor; | |
247 while (!shutdown_) { | 286 while (!shutdown_) { |
| 287 intptr_t r = wait_ml.WaitMicros(current_wait_time_); |
| 288 |
| 289 if ((r == Monitor::kNotified) && InDeepSleep()) { |
| 290 // Woken up from deep sleep. |
| 291 ASSERT(visitor.profiled_thread_count() == 0); |
| 292 // Return to regular interrupts. |
| 293 current_wait_time_ = interrupt_period_; |
| 294 } |
| 295 |
| 296 // Reset count before visiting isolates. |
| 297 visitor.set_profiled_thread_count(0); |
248 Isolate::VisitIsolates(&visitor); | 298 Isolate::VisitIsolates(&visitor); |
249 wait_ml.WaitMicros(interrupt_period_); | 299 |
| 300 if (visitor.profiled_thread_count() == 0) { |
| 301 // No isolates were profiled. In order to reduce unnecessary CPU |
| 302 // load, we will wait until we are notified before attempting to |
| 303 // interrupt again. |
| 304 current_wait_time_ = Monitor::kNoTimeout; |
| 305 continue; |
| 306 } |
| 307 |
| 308 ASSERT(current_wait_time_ != Monitor::kNoTimeout); |
250 } | 309 } |
251 } | 310 } |
252 if (FLAG_trace_thread_interrupter) { | 311 if (FLAG_trace_thread_interrupter) { |
253 OS::Print("ThreadInterrupter thread exiting.\n"); | 312 OS::Print("ThreadInterrupter thread exiting.\n"); |
254 } | 313 } |
255 { | 314 { |
256 // Signal to main thread we are exiting. | 315 // Signal to main thread we are exiting. |
257 MonitorLocker shutdown_ml(monitor_); | 316 MonitorLocker shutdown_ml(monitor_); |
258 thread_running_ = false; | 317 thread_running_ = false; |
259 shutdown_ml.Notify(); | 318 shutdown_ml.Notify(); |
260 } | 319 } |
261 } | 320 } |
262 | 321 |
263 } // namespace dart | 322 } // namespace dart |
OLD | NEW |