Flutter Engine
The Flutter Engine
thread_interrupter.cc
Go to the documentation of this 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
3// BSD-style license that can be found in the LICENSE file.
4
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 Linux
24// and Android, is SIGPROF. The second, used on Windows, Fuchsia, Mac and iOS,
25// is explicit suspend and resume thread system calls. (Although Mac supports
26// SIGPROF, when the process is attached to lldb, it becomes unusably slow, and
27// signal masking is unreliable across fork-exec.) Signal delivery forbids
28// taking locks and allocating memory (which takes a lock). Explicit suspend and
29// resume means that the interrupt callback will not be executing on the
30// interrupted thread, making it meaningless to access TLS from within the
31// thread interrupt callback. Combining these limitations, thread interrupt
32// callbacks are forbidden from:
33//
34// * Accessing TLS.
35// * Allocating memory.
36// * Taking a lock.
37//
38// The ThreadInterrupter has a single monitor (monitor_). This monitor is used
39// to synchronize startup, shutdown, and waking up from a deep sleep.
40//
41
42DEFINE_FLAG(bool, trace_thread_interrupter, false, "Trace thread interrupter");
43
44bool ThreadInterrupter::initialized_ = false;
45bool ThreadInterrupter::shutdown_ = false;
46bool ThreadInterrupter::thread_running_ = false;
47bool ThreadInterrupter::woken_up_ = false;
48ThreadJoinId ThreadInterrupter::interrupter_thread_id_ =
50Monitor* ThreadInterrupter::monitor_ = nullptr;
51intptr_t ThreadInterrupter::interrupt_period_ = 1000;
52intptr_t ThreadInterrupter::current_wait_time_ = Monitor::kNoTimeout;
53
55 ASSERT(!initialized_);
56 if (monitor_ == nullptr) {
57 monitor_ = new Monitor();
58 }
59 ASSERT(monitor_ != nullptr);
60 initialized_ = true;
61 shutdown_ = false;
62}
63
65 ASSERT(initialized_);
66 if (FLAG_trace_thread_interrupter) {
67 OS::PrintErr("ThreadInterrupter starting up.\n");
68 }
69 ASSERT(interrupter_thread_id_ == OSThread::kInvalidThreadJoinId);
70 {
71 MonitorLocker startup_ml(monitor_);
72 OSThread::Start("Dart Profiler ThreadInterrupter", ThreadMain, 0);
73 while (!thread_running_) {
74 startup_ml.Wait();
75 }
76 }
77 ASSERT(interrupter_thread_id_ != OSThread::kInvalidThreadJoinId);
78 if (FLAG_trace_thread_interrupter) {
79 OS::PrintErr("ThreadInterrupter running.\n");
80 }
81}
82
84 {
85 MonitorLocker shutdown_ml(monitor_);
86 if (shutdown_) {
87 // Already shutdown.
88 return;
89 }
90 shutdown_ = true;
91 // Notify.
92 shutdown_ml.Notify();
93 ASSERT(initialized_);
94 if (FLAG_trace_thread_interrupter) {
95 OS::PrintErr("ThreadInterrupter shutting down.\n");
96 }
97 }
98
99 // Join the thread.
100 ASSERT(interrupter_thread_id_ != OSThread::kInvalidThreadJoinId);
101 OSThread::Join(interrupter_thread_id_);
102 interrupter_thread_id_ = OSThread::kInvalidThreadJoinId;
103 initialized_ = false;
104
105 if (FLAG_trace_thread_interrupter) {
106 OS::PrintErr("ThreadInterrupter shut down.\n");
107 }
108}
109
110// Delay between interrupts.
112 if (!initialized_) {
113 // Profiler may not be enabled.
114 return;
115 }
116 MonitorLocker ml(monitor_);
117 if (shutdown_) {
118 return;
119 }
120 ASSERT(initialized_);
121 ASSERT(period > 0);
122 interrupt_period_ = period;
123}
124
126 if (monitor_ == nullptr) {
127 // Early call.
128 return;
129 }
130 {
131 MonitorLocker ml(monitor_);
132 if (shutdown_) {
133 // Late call
134 return;
135 }
136 if (!initialized_) {
137 // Early call.
138 return;
139 }
140 woken_up_ = true;
141 if (!InDeepSleep()) {
142 // No need to notify, regularly waking up.
143 return;
144 }
145 // Notify the interrupter to wake it from its deep sleep.
146 ml.Notify();
147 }
148}
149
150void ThreadInterrupter::ThreadMain(uword parameters) {
151 ASSERT(initialized_);
152 InstallSignalHandler();
153 if (FLAG_trace_thread_interrupter) {
154 OS::PrintErr("ThreadInterrupter thread running.\n");
155 }
156 {
157 // Signal to main thread we are ready.
158 MonitorLocker startup_ml(monitor_);
159 OSThread* os_thread = OSThread::Current();
160 ASSERT(os_thread != nullptr);
161 interrupter_thread_id_ = OSThread::GetCurrentThreadJoinId(os_thread);
162 thread_running_ = true;
163 startup_ml.Notify();
164 }
165 {
166 intptr_t interrupted_thread_count = 0;
167 MonitorLocker wait_ml(monitor_);
168 current_wait_time_ = interrupt_period_;
169 while (!shutdown_) {
170 intptr_t r = wait_ml.WaitMicros(current_wait_time_);
171
172 if (shutdown_) {
173 break;
174 }
175
176 if ((r == Monitor::kNotified) && InDeepSleep()) {
177 // Woken up from deep sleep.
178 ASSERT(interrupted_thread_count == 0);
179 // Return to regular interrupts.
180 current_wait_time_ = interrupt_period_;
181 } else if (current_wait_time_ != interrupt_period_) {
182 // The interrupt period may have been updated via the service protocol.
183 current_wait_time_ = interrupt_period_;
184 }
185
186 // Reset count before interrupting any threads.
187 interrupted_thread_count = 0;
188
189 // Temporarily drop the monitor while we interrupt threads.
190 wait_ml.Exit();
191
192 {
193 OSThreadIterator it;
194 while (it.HasNext()) {
195 OSThread* thread = it.Next();
196 if (thread->ThreadInterruptsEnabled()) {
197 interrupted_thread_count++;
198 InterruptThread(thread);
199 }
200 }
201 }
202
203 // Take the monitor lock again.
204 wait_ml.Enter();
205
206 // Now that we have the lock, check if we were signaled to wake up while
207 // interrupting threads.
208 if (!woken_up_ && (interrupted_thread_count == 0)) {
209 // No threads were interrupted and we were not signaled to interrupt
210 // new threads. In order to reduce unnecessary CPU load, we will wait
211 // until we are notified before attempting to interrupt again.
212 current_wait_time_ = Monitor::kNoTimeout;
213 continue;
214 }
215
216 woken_up_ = false;
217
218 ASSERT(current_wait_time_ != Monitor::kNoTimeout);
219 }
220 }
221 RemoveSignalHandler();
222 if (FLAG_trace_thread_interrupter) {
223 OS::PrintErr("ThreadInterrupter thread exiting.\n");
224 }
225 {
226 // Signal to main thread we are exiting.
227 MonitorLocker shutdown_ml(monitor_);
228 thread_running_ = false;
229 shutdown_ml.Notify();
230 }
231}
232
233#if !defined(DART_HOST_OS_ANDROID)
235 return nullptr;
236}
237
239#endif
240
241#endif // !PRODUCT
242
243} // namespace dart
Monitor::WaitResult Wait(int64_t millis=Monitor::kNoTimeout)
Definition: lockers.h:172
static constexpr int64_t kNoTimeout
Definition: os_thread.h:361
static int Start(const char *name, ThreadStartFunction function, uword parameter)
static void Join(ThreadJoinId id)
static OSThread * Current()
Definition: os_thread.h:179
static ThreadJoinId GetCurrentThreadJoinId(OSThread *thread)
static const ThreadJoinId kInvalidThreadJoinId
Definition: os_thread.h:249
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static void CleanupCurrentThreadState(void *state)
static void SetInterruptPeriod(intptr_t period)
static void * PrepareCurrentThread()
static void InterruptThread(OSThread *thread)
#define ASSERT(E)
AtkStateType state
Definition: dart_vm.cc:33
pthread_t ThreadJoinId
uintptr_t uword
Definition: globals.h:501
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")