| 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 |