Flutter Engine
The Flutter Engine
safepoint.cc
Go to the documentation of this file.
1// Copyright (c) 2016, 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/heap/safepoint.h"
6
7#include "vm/heap/heap.h"
8#include "vm/thread.h"
10
11namespace dart {
12
13DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic.");
14
17 : ThreadStackResource(T), level_(level) {
18 ASSERT(T != nullptr && T->isolate_group() != nullptr);
19
20 auto handler = T->isolate_group()->safepoint_handler();
21 handler->SafepointThreads(T, level_);
22}
23
25 Thread* T = thread();
26 ASSERT(T != nullptr && T->isolate_group() != nullptr);
27
28 auto handler = T->isolate_group()->safepoint_handler();
29 handler->ResumeThreads(T, level_);
30}
31
33 Thread* T,
35 : ThreadStackResource(T), level_(level) {
36 ASSERT(T != nullptr);
37 IsolateGroup* IG = T->isolate_group();
38 ASSERT(IG != nullptr);
39
40 T->IncrementForceGrowthScopeDepth();
41
42 auto handler = IG->safepoint_handler();
43 handler->SafepointThreads(T, level_);
44}
45
47 Thread* T = thread();
48 ASSERT(T != nullptr);
49 IsolateGroup* IG = T->isolate_group();
50 ASSERT(IG != nullptr);
51
52 auto handler = IG->safepoint_handler();
53 handler->ResumeThreads(T, level_);
54
55 T->DecrementForceGrowthScopeDepth();
56 if (!T->force_growth()) {
57 // Check if we passed the growth limit during the scope.
58 T->heap()->CheckCatchUp(T);
59 }
60}
61
63 : isolate_group_(isolate_group) {
64 handlers_[SafepointLevel::kGC] =
65 new LevelHandler(isolate_group, SafepointLevel::kGC);
67 new LevelHandler(isolate_group, SafepointLevel::kGCAndDeopt);
69 new LevelHandler(isolate_group, SafepointLevel::kGCAndDeoptAndReload);
70}
71
73 for (intptr_t level = 0; level < SafepointLevel::kNumLevels; ++level) {
74 ASSERT(handlers_[level]->owner_ == nullptr);
75 delete handlers_[level];
76 }
77}
78
79void SafepointHandler::SafepointThreads(Thread* T, SafepointLevel level) {
80 ASSERT(T->no_safepoint_scope_depth() == 0);
81 ASSERT(T->execution_state() == Thread::kThreadInVM);
82 ASSERT(T->current_safepoint_level() >= level);
83
85 {
86 MonitorLocker tl(threads_lock());
87
88 // Allow recursive deopt safepoint operation.
89 if (handlers_[level]->owner_ == T) {
90 // If we own this safepoint level already we have to own the lower levels
91 // as well.
92 AssertWeOwnLowerLevelSafepoints(T, level);
93
94 for (intptr_t i = 0; i <= level; ++i) {
95 handlers_[i]->operation_count_++;
96 }
97 return;
98 }
99
100 // This level of nesting is not allowed (this thread cannot own lower levels
101 // and then later try acquire higher levels).
102 AssertWeDoNotOwnLowerLevelSafepoints(T, level);
103
104 // Mark this thread at safepoint and possibly notify waiting threads.
105 {
106 MonitorLocker tl(T->thread_lock());
107 // We only enter [level] here. That means a higher level that is waiting
108 // for us to check-in will not consider us as not parked. This is required
109 // since we are not actually parked (we can finish running this method and
110 // then caller continues).
111 EnterSafepointLocked(T, &tl, level);
112 }
113
114 // Wait until other safepoint operations are done & mark us as owning
115 // the safepoint - so no other thread can.
116 while (handlers_[level]->SafepointInProgress()) {
117 tl.Wait();
118 }
119 handlers_[level]->SetSafepointInProgress(T);
120
121 // Ensure a thread is at a safepoint or notify it to get to one.
122 handlers_[level]->NotifyThreadsToGetToSafepointLevel(T, &oob_isolates);
123 }
124
125 for (auto main_port : oob_isolates) {
127 /*ignored=*/-1);
128 }
129
130 // Now wait for all threads that are not already at a safepoint to check-in.
131 handlers_[level]->WaitUntilThreadsReachedSafepointLevel();
132
133 // No other mutator is running at this point. We'll set ourselves as owners of
134 // all the lower levels as well - since higher levels provide even more
135 // guarantees that lower levels (e.g. others being stopped at places where
136 // one can deopt also implies one can gc)
137 AcquireLowerLevelSafepoints(T, level);
138
139 // The current thread owns the safepoint, but it will continue to run and as
140 // such is not at any "point" that can be considered safe.
141 {
142 MonitorLocker tl(T->thread_lock());
143 ExitSafepointLocked(T, &tl, level);
144 }
145}
146
147void SafepointHandler::AssertWeOwnLowerLevelSafepoints(Thread* T,
149 for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) {
150 RELEASE_ASSERT(handlers_[lower_level]->owner_ == T);
151 }
152}
153
154void SafepointHandler::AssertWeDoNotOwnLowerLevelSafepoints(
155 Thread* T,
157 for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) {
158 RELEASE_ASSERT(handlers_[lower_level]->owner_ != T);
159 }
160}
161
162void SafepointHandler::LevelHandler::NotifyThreadsToGetToSafepointLevel(
163 Thread* T,
164 MallocGrowableArray<Dart_Port>* oob_isolates) {
165 ASSERT(num_threads_not_parked_ == 0);
166 for (auto current = isolate_group()->thread_registry()->active_list();
167 current != nullptr; current = current->next()) {
168 MonitorLocker tl(current->thread_lock());
169 if (!current->BypassSafepoints() && current != T) {
170 const uint32_t state = current->SetSafepointRequested(level_, true);
171 if (!Thread::IsAtSafepoint(level_, state)) {
173 // Interrupt the mutator by sending an reload OOB message. The
174 // mutator will only check-in once it's handling the reload OOB
175 // message.
176 //
177 // If there's no isolate, it may be a helper thread that has entered
178 // via `Thread::EnterIsolateGroupAsHelper()`. In that case we cannot
179 // send an OOB message. Instead we'll have to wait until that thread
180 // de-schedules itself.
181 auto isolate = current->scheduled_dart_mutator_isolate();
182 if (isolate != nullptr) {
183 oob_isolates->Add(isolate->main_port());
184 }
185 } else {
186 // Interrupt the mutator and ask it to block at any interruption
187 // point.
188 if (current->IsDartMutatorThread()) {
189 current->ScheduleInterrupts(Thread::kVMInterrupt);
190 }
191 }
192 MonitorLocker sl(&parked_lock_);
193 num_threads_not_parked_++;
194 }
195 }
196 }
197}
198
199void SafepointHandler::ResumeThreads(Thread* T, SafepointLevel level) {
200 {
201 MonitorLocker sl(threads_lock());
202
203 ASSERT(handlers_[level]->SafepointInProgress());
204 ASSERT(handlers_[level]->owner_ == T);
205 AssertWeOwnLowerLevelSafepoints(T, level);
206
207 // We allow recursive safepoints.
208 if (handlers_[level]->operation_count_ > 1) {
209 for (intptr_t i = 0; i <= level; ++i) {
210 handlers_[i]->operation_count_--;
211 }
212 return;
213 }
214
215 ReleaseLowerLevelSafepoints(T, level);
216 handlers_[level]->ResetSafepointInProgress(T);
217 handlers_[level]->NotifyThreadsToContinue(T);
218 sl.NotifyAll();
219 }
220}
221
222void SafepointHandler::LevelHandler::WaitUntilThreadsReachedSafepointLevel() {
223 MonitorLocker sl(&parked_lock_);
224 intptr_t num_attempts = 0;
225 while (num_threads_not_parked_ > 0) {
226 Monitor::WaitResult retval = sl.Wait(1000);
227 if (retval == Monitor::kTimedOut) {
228 num_attempts += 1;
229 if (FLAG_trace_safepoint && num_attempts > 10) {
230 for (auto current = isolate_group()->thread_registry()->active_list();
231 current != nullptr; current = current->next()) {
232 if (!current->IsAtSafepoint(level_)) {
233 OS::PrintErr("Attempt:%" Pd " waiting for thread %s to check in\n",
234 num_attempts, current->os_thread()->name());
235 }
236 }
237 }
238 }
239 }
240}
241
242void SafepointHandler::AcquireLowerLevelSafepoints(Thread* T,
244 MonitorLocker tl(threads_lock());
245 ASSERT(handlers_[level]->owner_ == T);
246 for (intptr_t lower_level = level - 1; lower_level >= 0; --lower_level) {
247 ASSERT(!handlers_[lower_level]->SafepointInProgress());
248 handlers_[lower_level]->SetSafepointInProgress(T);
249 ASSERT(handlers_[lower_level]->owner_ == T);
250 }
251}
252
253void SafepointHandler::ReleaseLowerLevelSafepoints(Thread* T,
255 for (intptr_t lower_level = 0; lower_level < level; ++lower_level) {
256 handlers_[lower_level]->ResetSafepointInProgress(T);
257 }
258}
259
260void SafepointHandler::LevelHandler::NotifyThreadsToContinue(Thread* T) {
261 for (auto current = isolate_group()->thread_registry()->active_list();
262 current != nullptr; current = current->next()) {
263 MonitorLocker tl(current->thread_lock());
264 if (!current->BypassSafepoints() && current != T) {
265 bool resume = false;
266 for (intptr_t lower_level = level_; lower_level >= 0; --lower_level) {
267 if (Thread::IsBlockedForSafepoint(current->SetSafepointRequested(
268 static_cast<SafepointLevel>(lower_level), false))) {
269 resume = true;
270 }
271 }
272 if (resume) {
273 tl.Notify();
274 }
275 }
276 }
277}
278
280 MonitorLocker tl(T->thread_lock());
281 EnterSafepointLocked(T, &tl, T->current_safepoint_level());
282}
283
285 MonitorLocker tl(T->thread_lock());
286 ASSERT(T->IsAtSafepoint());
287 ExitSafepointLocked(T, &tl, T->current_safepoint_level());
288 ASSERT(!T->IsSafepointRequestedLocked(T->current_safepoint_level()));
289}
290
292 const Thread* current_thread) const {
293 // The [current_thread] may not own the active safepoint.
294 intptr_t last_count = -1;
296
297 // Notice: We access SafepointLevel::{owner_,operation_count_} fields
298 // without lock. This is ok since:
299 //
300 // * If the current thread is the owner, then it will be the one that has
301 // last written `Thread::Current()` to the `owner_` field (as well as
302 // updated the `operation_count_`) - nobody else can update those fields
303 // in the meantime. Once the current thread exits we set it to `nullptr`.
304 //
305 // * If there's no owner or another thread is the owner the value cannot be
306 // `Thread::Current()`: only our thread can write that particular value.
307 //
308 // => Even if there's racy writes by another thread, the logic is still
309 // safe.
310 //
311 for (intptr_t level = 0; level < SafepointLevel::kNumLevels; ++level) {
312 if (handlers_[level]->owner_ == current_thread) {
313 const intptr_t count = handlers_[level]->operation_count_;
314 if (count < last_count) return last_level;
315 last_count = count;
316 last_level = static_cast<SafepointLevel>(level);
317 } else {
318 return last_level;
319 }
320 }
321 return last_level;
322}
323
325 ASSERT(!T->BypassSafepoints());
326 MonitorLocker tl(T->thread_lock());
327 // This takes into account the safepoint level the thread can participate in.
328 const SafepointLevel level = T->current_safepoint_level();
329 if (T->IsSafepointRequestedLocked(level)) {
330 EnterSafepointLocked(T, &tl, level);
331 ExitSafepointLocked(T, &tl, level);
332 ASSERT(!T->IsSafepointRequestedLocked(level));
333 }
334}
335
336void SafepointHandler::EnterSafepointLocked(Thread* T,
337 MonitorLocker* tl,
339 T->SetAtSafepoint(true, level);
340 // Several safepointing operations (at different) levels may happen at same
341 // time. Ensure we notify all of them that we are parked now.
342 for (intptr_t i = 0; i <= level; ++i) {
343 if (T->IsSafepointLevelRequestedLocked(static_cast<SafepointLevel>(i))) {
344 handlers_[i]->NotifyWeAreParked(T);
345 }
346 }
347}
348
349void SafepointHandler::LevelHandler::NotifyWeAreParked(Thread* T) {
350 ASSERT(owner_ != nullptr);
351 MonitorLocker sl(&parked_lock_);
352 ASSERT(num_threads_not_parked_ > 0);
353 num_threads_not_parked_ -= 1;
354 if (num_threads_not_parked_ == 0) {
355 sl.Notify();
356 }
357}
358
359void SafepointHandler::ExitSafepointLocked(Thread* T,
360 MonitorLocker* tl,
362 while (T->IsSafepointRequestedLocked(level)) {
363 T->SetBlockedForSafepoint(true);
364 tl->Wait();
365 T->SetBlockedForSafepoint(false);
366 }
367 T->SetAtSafepoint(false, level);
368}
369
370} // namespace dart
int count
Definition: FontMgrTest.cpp:50
#define IG
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
ForceGrowthSafepointOperationScope(Thread *T, SafepointLevel level)
Definition: safepoint.cc:32
@ kCheckForReload
Definition: isolate.h:975
void SendInternalLibMessage(LibMsgId msg_id, uint64_t capability)
Definition: isolate.cc:1033
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
SafepointLevel InnermostSafepointOperation(const Thread *current_thread) const
Definition: safepoint.cc:291
SafepointHandler(IsolateGroup *I)
Definition: safepoint.cc:62
void EnterSafepointUsingLock(Thread *T)
Definition: safepoint.cc:279
void BlockForSafepoint(Thread *T)
Definition: safepoint.cc:324
void ExitSafepointUsingLock(Thread *T)
Definition: safepoint.cc:284
SafepointOperationScope(Thread *T, SafepointLevel level)
Definition: safepoint.cc:15
bool IsBlockedForSafepoint() const
Definition: thread.h:999
@ kVMInterrupt
Definition: thread.h:488
bool IsAtSafepoint() const
Definition: thread.h:917
#define ASSERT(E)
AtkStateType state
Definition: dart_vm.cc:33
SafepointLevel
Definition: thread.h:289
@ kGC
Definition: thread.h:291
@ kNumLevels
Definition: thread.h:297
@ kNoSafepoint
Definition: thread.h:300
@ kGCAndDeoptAndReload
Definition: thread.h:295
@ kGCAndDeopt
Definition: thread.h:293
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
#define Pd
Definition: globals.h:408
#define T
Definition: precompiler.cc:65