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#ifndef RUNTIME_VM_HEAP_SAFEPOINT_H_
6#define RUNTIME_VM_HEAP_SAFEPOINT_H_
7
8#include "vm/globals.h"
9#include "vm/isolate.h"
10#include "vm/lockers.h"
11#include "vm/thread.h"
12#include "vm/thread_stack_resource.h"
13
14namespace dart {
15
16// A stack based scope that can be used to perform an operation after getting
17// all threads to a safepoint. At the end of the operation all the threads are
18// resumed.
19class SafepointOperationScope : public ThreadStackResource {
20 public:
21 explicit SafepointOperationScope(Thread* T);
22 ~SafepointOperationScope();
23
24 private:
25 DISALLOW_COPY_AND_ASSIGN(SafepointOperationScope);
26};
27
28// A stack based scope that can be used to perform an operation after getting
29// all threads to a safepoint. At the end of the operation all the threads are
30// resumed. Allocations in the scope will force heap growth.
31class ForceGrowthSafepointOperationScope : public ThreadStackResource {
32 public:
33 explicit ForceGrowthSafepointOperationScope(Thread* T);
34 ~ForceGrowthSafepointOperationScope();
35
36 private:
37 bool current_growth_controller_state_;
38
39 DISALLOW_COPY_AND_ASSIGN(ForceGrowthSafepointOperationScope);
40};
41
42// Implements handling of safepoint operations for all threads in an
43// IsolateGroup.
44class SafepointHandler {
45 public:
46 explicit SafepointHandler(IsolateGroup* I);
47 ~SafepointHandler();
48
49 void EnterSafepointUsingLock(Thread* T);
50 void ExitSafepointUsingLock(Thread* T);
51
52 void BlockForSafepoint(Thread* T);
53
54 bool IsOwnedByTheThread(Thread* thread) { return owner_ == thread; }
55
56 private:
57 void SafepointThreads(Thread* T);
58 void ResumeThreads(Thread* T);
59
60 IsolateGroup* isolate_group() const { return isolate_group_; }
61 Monitor* threads_lock() const { return isolate_group_->threads_lock(); }
62 bool SafepointInProgress() const {
63 ASSERT(threads_lock()->IsOwnedByCurrentThread());
64 return ((safepoint_operation_count_ > 0) && (owner_ != NULL));
65 }
66 void SetSafepointInProgress(Thread* T) {
67 ASSERT(threads_lock()->IsOwnedByCurrentThread());
68 ASSERT(owner_ == NULL);
69 ASSERT(safepoint_operation_count_ == 0);
70 safepoint_operation_count_ = 1;
71 owner_ = T;
72 }
73 void ResetSafepointInProgress(Thread* T) {
74 ASSERT(threads_lock()->IsOwnedByCurrentThread());
75 ASSERT(owner_ == T);
76 ASSERT(safepoint_operation_count_ == 1);
77 safepoint_operation_count_ = 0;
78 owner_ = NULL;
79 }
80 int32_t safepoint_operation_count() const {
81 ASSERT(threads_lock()->IsOwnedByCurrentThread());
82 return safepoint_operation_count_;
83 }
84 void increment_safepoint_operation_count() {
85 ASSERT(threads_lock()->IsOwnedByCurrentThread());
86 ASSERT(safepoint_operation_count_ < kMaxInt32);
87 safepoint_operation_count_ += 1;
88 }
89 void decrement_safepoint_operation_count() {
90 ASSERT(threads_lock()->IsOwnedByCurrentThread());
91 ASSERT(safepoint_operation_count_ > 0);
92 safepoint_operation_count_ -= 1;
93 }
94
95 IsolateGroup* isolate_group_;
96
97 // Monitor used by thread initiating a safepoint operation to track threads
98 // not at a safepoint and wait for these threads to reach a safepoint.
99 Monitor safepoint_lock_;
100 int32_t number_threads_not_at_safepoint_;
101
102 // Count that indicates if a safepoint operation is currently in progress
103 // and also tracks the number of recursive safepoint operations on the
104 // same thread.
105 int32_t safepoint_operation_count_;
106
107 // If a safepoint operation is currently in progress, this field contains
108 // the thread that initiated the safepoint operation, otherwise it is NULL.
109 Thread* owner_;
110
111 friend class Isolate;
112 friend class IsolateGroup;
113 friend class SafepointOperationScope;
114 friend class ForceGrowthSafepointOperationScope;
115 friend class HeapIterationScope;
116};
117
118/*
119 * Set of StackResource classes to track thread execution state transitions:
120 *
121 * kThreadInGenerated transitioning to
122 * ==> kThreadInVM:
123 * - set_execution_state(kThreadInVM).
124 * - block if safepoint is requested.
125 * ==> kThreadInNative:
126 * - set_execution_state(kThreadInNative).
127 * - EnterSafepoint().
128 * ==> kThreadInBlockedState:
129 * - Invalid transition
130 *
131 * kThreadInVM transitioning to
132 * ==> kThreadInGenerated
133 * - set_execution_state(kThreadInGenerated).
134 * ==> kThreadInNative
135 * - set_execution_state(kThreadInNative).
136 * - EnterSafepoint.
137 * ==> kThreadInBlockedState
138 * - set_execution_state(kThreadInBlockedState).
139 * - EnterSafepoint.
140 *
141 * kThreadInNative transitioning to
142 * ==> kThreadInGenerated
143 * - ExitSafepoint.
144 * - set_execution_state(kThreadInGenerated).
145 * ==> kThreadInVM
146 * - ExitSafepoint.
147 * - set_execution_state(kThreadInVM).
148 * ==> kThreadInBlocked
149 * - Invalid transition.
150 *
151 * kThreadInBlocked transitioning to
152 * ==> kThreadInVM
153 * - ExitSafepoint.
154 * - set_execution_state(kThreadInVM).
155 * ==> kThreadInNative
156 * - Invalid transition.
157 * ==> kThreadInGenerated
158 * - Invalid transition.
159 */
160class TransitionSafepointState : public ThreadStackResource {
161 public:
162 explicit TransitionSafepointState(Thread* T) : ThreadStackResource(T) {}
163 ~TransitionSafepointState() {}
164
165 SafepointHandler* handler() const {
166 ASSERT(thread()->isolate() != NULL);
167 ASSERT(thread()->isolate()->safepoint_handler() != NULL);
168 return thread()->isolate()->safepoint_handler();
169 }
170
171 private:
172 DISALLOW_COPY_AND_ASSIGN(TransitionSafepointState);
173};
174
175// TransitionGeneratedToVM is used to transition the safepoint state of a
176// thread from "running generated code" to "running vm code" and ensures
177// that the state is reverted back to "running generated code" when
178// exiting the scope/frame.
179class TransitionGeneratedToVM : public TransitionSafepointState {
180 public:
181 explicit TransitionGeneratedToVM(Thread* T) : TransitionSafepointState(T) {
182 ASSERT(T == Thread::Current());
183 ASSERT(T->execution_state() == Thread::kThreadInGenerated);
184 T->set_execution_state(Thread::kThreadInVM);
185 // Fast check to see if a safepoint is requested or not.
186 // We do the more expensive operation of blocking the thread
187 // only if a safepoint is requested.
188 if (T->IsSafepointRequested()) {
189 handler()->BlockForSafepoint(T);
190 }
191 }
192
193 ~TransitionGeneratedToVM() {
194 ASSERT(thread()->execution_state() == Thread::kThreadInVM);
195 thread()->set_execution_state(Thread::kThreadInGenerated);
196 }
197
198 private:
199 DISALLOW_COPY_AND_ASSIGN(TransitionGeneratedToVM);
200};
201
202// TransitionGeneratedToNative is used to transition the safepoint state of a
203// thread from "running generated code" to "running native code" and ensures
204// that the state is reverted back to "running generated code" when
205// exiting the scope/frame.
206class TransitionGeneratedToNative : public TransitionSafepointState {
207 public:
208 explicit TransitionGeneratedToNative(Thread* T)
209 : TransitionSafepointState(T) {
210 // Native code is considered to be at a safepoint and so we mark it
211 // accordingly.
212 ASSERT(T->execution_state() == Thread::kThreadInGenerated);
213 T->set_execution_state(Thread::kThreadInNative);
214 T->EnterSafepoint();
215 }
216
217 ~TransitionGeneratedToNative() {
218 // We are returning to generated code and so we are not at a safepoint
219 // anymore.
220 ASSERT(thread()->execution_state() == Thread::kThreadInNative);
221 thread()->ExitSafepoint();
222 thread()->set_execution_state(Thread::kThreadInGenerated);
223 }
224
225 private:
226 DISALLOW_COPY_AND_ASSIGN(TransitionGeneratedToNative);
227};
228
229// TransitionVMToBlocked is used to transition the safepoint state of a
230// thread from "running vm code" to "blocked on a monitor" and ensures
231// that the state is reverted back to "running vm code" when
232// exiting the scope/frame.
233class TransitionVMToBlocked : public TransitionSafepointState {
234 public:
235 explicit TransitionVMToBlocked(Thread* T) : TransitionSafepointState(T) {
236 // A thread blocked on a monitor is considered to be at a safepoint.
237 ASSERT(T->execution_state() == Thread::kThreadInVM);
238 T->set_execution_state(Thread::kThreadInBlockedState);
239 T->EnterSafepoint();
240 }
241
242 ~TransitionVMToBlocked() {
243 // We are returning to vm code and so we are not at a safepoint anymore.
244 ASSERT(thread()->execution_state() == Thread::kThreadInBlockedState);
245 thread()->ExitSafepoint();
246 thread()->set_execution_state(Thread::kThreadInVM);
247 }
248
249 private:
250 DISALLOW_COPY_AND_ASSIGN(TransitionVMToBlocked);
251};
252
253// TransitionVMToNative is used to transition the safepoint state of a
254// thread from "running vm code" to "running native code" and ensures
255// that the state is reverted back to "running vm code" when
256// exiting the scope/frame.
257class TransitionVMToNative : public TransitionSafepointState {
258 public:
259 explicit TransitionVMToNative(Thread* T) : TransitionSafepointState(T) {
260 // A thread running native code is considered to be at a safepoint.
261 ASSERT(T->execution_state() == Thread::kThreadInVM);
262 T->set_execution_state(Thread::kThreadInNative);
263 T->EnterSafepoint();
264 }
265
266 ~TransitionVMToNative() {
267 // We are returning to vm code and so we are not at a safepoint anymore.
268 ASSERT(thread()->execution_state() == Thread::kThreadInNative);
269 thread()->ExitSafepoint();
270 thread()->set_execution_state(Thread::kThreadInVM);
271 }
272
273 private:
274 DISALLOW_COPY_AND_ASSIGN(TransitionVMToNative);
275};
276
277// TransitionVMToGenerated is used to transition the safepoint state of a
278// thread from "running vm code" to "running generated code" and ensures
279// that the state is reverted back to "running vm code" when
280// exiting the scope/frame.
281class TransitionVMToGenerated : public TransitionSafepointState {
282 public:
283 explicit TransitionVMToGenerated(Thread* T) : TransitionSafepointState(T) {
284 ASSERT(T == Thread::Current());
285 ASSERT(T->execution_state() == Thread::kThreadInVM);
286 T->set_execution_state(Thread::kThreadInGenerated);
287 }
288
289 ~TransitionVMToGenerated() {
290 ASSERT(thread()->execution_state() == Thread::kThreadInGenerated);
291 thread()->set_execution_state(Thread::kThreadInVM);
292 // Fast check to see if a safepoint is requested or not.
293 // We do the more expensive operation of blocking the thread
294 // only if a safepoint is requested.
295 if (thread()->IsSafepointRequested()) {
296 handler()->BlockForSafepoint(thread());
297 }
298 }
299
300 private:
301 DISALLOW_COPY_AND_ASSIGN(TransitionVMToGenerated);
302};
303
304// TransitionNativeToVM is used to transition the safepoint state of a
305// thread from "running native code" to "running vm code" and ensures
306// that the state is reverted back to "running native code" when
307// exiting the scope/frame.
308class TransitionNativeToVM : public TransitionSafepointState {
309 public:
310 explicit TransitionNativeToVM(Thread* T) : TransitionSafepointState(T) {
311 // We are about to execute vm code and so we are not at a safepoint anymore.
312 ASSERT(T->execution_state() == Thread::kThreadInNative);
313 if (T->no_callback_scope_depth() == 0) {
314 T->ExitSafepoint();
315 }
316 T->set_execution_state(Thread::kThreadInVM);
317 }
318
319 ~TransitionNativeToVM() {
320 // We are returning to native code and so we are at a safepoint.
321 ASSERT(thread()->execution_state() == Thread::kThreadInVM);
322 thread()->set_execution_state(Thread::kThreadInNative);
323 if (thread()->no_callback_scope_depth() == 0) {
324 thread()->EnterSafepoint();
325 }
326 }
327
328 private:
329 DISALLOW_COPY_AND_ASSIGN(TransitionNativeToVM);
330};
331
332// TransitionToGenerated is used to transition the safepoint state of a
333// thread from "running vm code" or "running native code" to
334// "running generated code" and ensures that the state is reverted back
335// to "running vm code" or "running native code" when exiting the
336// scope/frame.
337class TransitionToGenerated : public TransitionSafepointState {
338 public:
339 explicit TransitionToGenerated(Thread* T)
340 : TransitionSafepointState(T), execution_state_(T->execution_state()) {
341 ASSERT(T == Thread::Current());
342 ASSERT((execution_state_ == Thread::kThreadInVM) ||
343 (execution_state_ == Thread::kThreadInNative));
344 if (execution_state_ == Thread::kThreadInNative) {
345 T->ExitSafepoint();
346 }
347 T->set_execution_state(Thread::kThreadInGenerated);
348 }
349
350 ~TransitionToGenerated() {
351 ASSERT(thread()->execution_state() == Thread::kThreadInGenerated);
352 if (execution_state_ == Thread::kThreadInNative) {
353 thread()->set_execution_state(Thread::kThreadInNative);
354 thread()->EnterSafepoint();
355 } else {
356 ASSERT(execution_state_ == Thread::kThreadInVM);
357 thread()->set_execution_state(Thread::kThreadInVM);
358 }
359 }
360
361 private:
362 uint32_t execution_state_;
363 DISALLOW_COPY_AND_ASSIGN(TransitionToGenerated);
364};
365
366// TransitionToVM is used to transition the safepoint state of a
367// thread from "running native code" to "running vm code"
368// and ensures that the state is reverted back to "running native code"
369// when exiting the scope/frame.
370// This transition helper is mainly used in the error path of the
371// Dart API implementations where we sometimes do not have an explicit
372// transition set up.
373class TransitionToVM : public TransitionSafepointState {
374 public:
375 explicit TransitionToVM(Thread* T)
376 : TransitionSafepointState(T), execution_state_(T->execution_state()) {
377 ASSERT(T == Thread::Current());
378 ASSERT((execution_state_ == Thread::kThreadInVM) ||
379 (execution_state_ == Thread::kThreadInNative));
380 if (execution_state_ == Thread::kThreadInNative) {
381 T->ExitSafepoint();
382 T->set_execution_state(Thread::kThreadInVM);
383 }
384 ASSERT(T->execution_state() == Thread::kThreadInVM);
385 }
386
387 ~TransitionToVM() {
388 ASSERT(thread()->execution_state() == Thread::kThreadInVM);
389 if (execution_state_ == Thread::kThreadInNative) {
390 thread()->set_execution_state(Thread::kThreadInNative);
391 thread()->EnterSafepoint();
392 }
393 }
394
395 private:
396 uint32_t execution_state_;
397 DISALLOW_COPY_AND_ASSIGN(TransitionToVM);
398};
399
400} // namespace dart
401
402#endif // RUNTIME_VM_HEAP_SAFEPOINT_H_
403