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

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, 7 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
« runtime/vm/profiler.cc ('K') | « 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_ = 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
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
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
OLDNEW
« runtime/vm/profiler.cc ('K') | « runtime/vm/thread_interrupter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698