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
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/thread_interrupter.h"
6
7#include "vm/flags.h"
8#include "vm/lockers.h"
9#include "vm/os.h"
10#include "vm/simulator.h"
11
12namespace dart {
13
14#ifndef PRODUCT
15
16// Notes:
17//
18// The ThreadInterrupter interrupts all threads actively running isolates once
19// per interrupt period (default is 1 millisecond). While the thread is
20// interrupted, the thread's interrupt callback is invoked. Callbacks cannot
21// rely on being executed on the interrupted thread.
22//
23// There are two mechanisms used to interrupt a thread. The first, used on OSs
24// with pthreads (Android, Linux, and Mac), is thread specific signal delivery.
25// The second, used on Windows, is explicit suspend and resume thread system
26// calls. Signal delivery forbids taking locks and allocating memory (which
27// takes a lock). Explicit suspend and resume means that the interrupt callback
28// will not be executing on the interrupted thread, making it meaningless to
29// access TLS from within the thread interrupt callback. Combining these
30// limitations, thread interrupt callbacks are forbidden from:
31//
32// * Accessing TLS.
33// * Allocating memory.
34// * Taking a lock.
35//
36// The ThreadInterrupter has a single monitor (monitor_). This monitor is used
37// to synchronize startup, shutdown, and waking up from a deep sleep.
38//
39
40DEFINE_FLAG(bool, trace_thread_interrupter, false, "Trace thread interrupter");
41
42bool ThreadInterrupter::initialized_ = false;
43bool ThreadInterrupter::shutdown_ = false;
44bool ThreadInterrupter::thread_running_ = false;
45bool ThreadInterrupter::woken_up_ = false;
46ThreadJoinId ThreadInterrupter::interrupter_thread_id_ =
47 OSThread::kInvalidThreadJoinId;
48Monitor* ThreadInterrupter::monitor_ = NULL;
49intptr_t ThreadInterrupter::interrupt_period_ = 1000;
50intptr_t ThreadInterrupter::current_wait_time_ = Monitor::kNoTimeout;
51// Note this initial state means there is one sample buffer reader. This
52// allows the EnterSampleReader during Cleanup (needed to ensure the buffer can
53// be safely freed) to be balanced by a ExitSampleReader during Init.
54std::atomic<intptr_t> ThreadInterrupter::sample_buffer_lock_ = {-1};
55std::atomic<intptr_t> ThreadInterrupter::sample_buffer_waiters_ = {1};
56
57void ThreadInterrupter::Init() {
58 ASSERT(!initialized_);
59 if (monitor_ == NULL) {
60 monitor_ = new Monitor();
61 }
62 ASSERT(monitor_ != NULL);
63 initialized_ = true;
64 shutdown_ = false;
65}
66
67void ThreadInterrupter::Startup() {
68 ASSERT(initialized_);
69 if (IsDebuggerAttached()) {
70 MonitorLocker shutdown_ml(monitor_);
71 shutdown_ = true;
72 if (FLAG_trace_thread_interrupter) {
73 OS::PrintErr(
74 "ThreadInterrupter disabled because a debugger is attached.\n");
75 }
76 return;
77 }
78 if (FLAG_trace_thread_interrupter) {
79 OS::PrintErr("ThreadInterrupter starting up.\n");
80 }
81 ASSERT(interrupter_thread_id_ == OSThread::kInvalidThreadJoinId);
82 {
83 MonitorLocker startup_ml(monitor_);
84 OSThread::Start("Dart Profiler ThreadInterrupter", ThreadMain, 0);
85 while (!thread_running_) {
86 startup_ml.Wait();
87 }
88 }
89 ASSERT(interrupter_thread_id_ != OSThread::kInvalidThreadJoinId);
90 if (FLAG_trace_thread_interrupter) {
91 OS::PrintErr("ThreadInterrupter running.\n");
92 }
93
94 ExitSampleReader();
95}
96
97void ThreadInterrupter::Cleanup() {
98 {
99 MonitorLocker shutdown_ml(monitor_);
100 if (shutdown_) {
101 // Already shutdown.
102 return;
103 }
104 shutdown_ = true;
105 // Notify.
106 shutdown_ml.Notify();
107 ASSERT(initialized_);
108 if (FLAG_trace_thread_interrupter) {
109 OS::PrintErr("ThreadInterrupter shutting down.\n");
110 }
111 }
112
113 // Join the thread.
114 ASSERT(interrupter_thread_id_ != OSThread::kInvalidThreadJoinId);
115 OSThread::Join(interrupter_thread_id_);
116 interrupter_thread_id_ = OSThread::kInvalidThreadJoinId;
117 initialized_ = false;
118
119 if (FLAG_trace_thread_interrupter) {
120 OS::PrintErr("ThreadInterrupter shut down.\n");
121 }
122
123 // Wait for outstanding signals.
124 EnterSampleReader();
125}
126
127// Delay between interrupts.
128void ThreadInterrupter::SetInterruptPeriod(intptr_t period) {
129 if (!initialized_) {
130 // Profiler may not be enabled.
131 return;
132 }
133 MonitorLocker ml(monitor_);
134 if (shutdown_) {
135 return;
136 }
137 ASSERT(initialized_);
138 ASSERT(period > 0);
139 interrupt_period_ = period;
140}
141
142void ThreadInterrupter::WakeUp() {
143 if (monitor_ == NULL) {
144 // Early call.
145 return;
146 }
147 {
148 MonitorLocker ml(monitor_);
149 if (shutdown_) {
150 // Late call
151 return;
152 }
153 if (!initialized_) {
154 // Early call.
155 return;
156 }
157 woken_up_ = true;
158 if (!InDeepSleep()) {
159 // No need to notify, regularly waking up.
160 return;
161 }
162 // Notify the interrupter to wake it from its deep sleep.
163 ml.Notify();
164 }
165}
166
167void ThreadInterrupter::ThreadMain(uword parameters) {
168 ASSERT(initialized_);
169 InstallSignalHandler();
170 if (FLAG_trace_thread_interrupter) {
171 OS::PrintErr("ThreadInterrupter thread running.\n");
172 }
173 {
174 // Signal to main thread we are ready.
175 MonitorLocker startup_ml(monitor_);
176 OSThread* os_thread = OSThread::Current();
177 ASSERT(os_thread != NULL);
178 interrupter_thread_id_ = OSThread::GetCurrentThreadJoinId(os_thread);
179 thread_running_ = true;
180 startup_ml.Notify();
181 }
182 {
183 intptr_t interrupted_thread_count = 0;
184 MonitorLocker wait_ml(monitor_);
185 current_wait_time_ = interrupt_period_;
186 while (!shutdown_) {
187 intptr_t r = wait_ml.WaitMicros(current_wait_time_);
188
189 if (shutdown_) {
190 break;
191 }
192
193 if ((r == Monitor::kNotified) && InDeepSleep()) {
194 // Woken up from deep sleep.
195 ASSERT(interrupted_thread_count == 0);
196 // Return to regular interrupts.
197 current_wait_time_ = interrupt_period_;
198 } else if (current_wait_time_ != interrupt_period_) {
199 // The interrupt period may have been updated via the service protocol.
200 current_wait_time_ = interrupt_period_;
201 }
202
203 // Reset count before interrupting any threads.
204 interrupted_thread_count = 0;
205
206 // Temporarily drop the monitor while we interrupt threads.
207 wait_ml.Exit();
208
209 {
210 OSThreadIterator it;
211 while (it.HasNext()) {
212 OSThread* thread = it.Next();
213 if (thread->ThreadInterruptsEnabled()) {
214 interrupted_thread_count++;
215 InterruptThread(thread);
216 }
217 }
218 }
219
220 // Take the monitor lock again.
221 wait_ml.Enter();
222
223 // Now that we have the lock, check if we were signaled to wake up while
224 // interrupting threads.
225 if (!woken_up_ && (interrupted_thread_count == 0)) {
226 // No threads were interrupted and we were not signaled to interrupt
227 // new threads. In order to reduce unnecessary CPU load, we will wait
228 // until we are notified before attempting to interrupt again.
229 current_wait_time_ = Monitor::kNoTimeout;
230 continue;
231 }
232
233 woken_up_ = false;
234
235 ASSERT(current_wait_time_ != Monitor::kNoTimeout);
236 }
237 }
238 RemoveSignalHandler();
239 if (FLAG_trace_thread_interrupter) {
240 OS::PrintErr("ThreadInterrupter thread exiting.\n");
241 }
242 {
243 // Signal to main thread we are exiting.
244 MonitorLocker shutdown_ml(monitor_);
245 thread_running_ = false;
246 shutdown_ml.Notify();
247 }
248}
249
250#endif // !PRODUCT
251
252} // namespace dart
253