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

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

Issue 297183003: Reduce CPU usage when no isolates need to be profiled. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 months 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
« no previous file with comments | « runtime/vm/thread_interrupter.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « runtime/vm/thread_interrupter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698