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"
9#include "vm/thread_registry.h"
10
11namespace dart {
12
13DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic.");
14
15SafepointOperationScope::SafepointOperationScope(Thread* T)
16 : ThreadStackResource(T) {
17 ASSERT(T != nullptr && T->isolate_group() != nullptr);
18
19 SafepointHandler* handler = T->isolate_group()->safepoint_handler();
20 ASSERT(handler != NULL);
21
22 // Signal all threads to get to a safepoint and wait for them to
23 // get to a safepoint.
24 handler->SafepointThreads(T);
25}
26
27SafepointOperationScope::~SafepointOperationScope() {
28 Thread* T = thread();
29 ASSERT(T != nullptr && T->isolate_group() != nullptr);
30
31 // Resume all threads which are blocked for the safepoint operation.
32 SafepointHandler* handler = T->isolate_group()->safepoint_handler();
33 ASSERT(handler != NULL);
34 handler->ResumeThreads(T);
35}
36
37ForceGrowthSafepointOperationScope::ForceGrowthSafepointOperationScope(
38 Thread* T)
39 : ThreadStackResource(T) {
40 ASSERT(T != NULL);
41 IsolateGroup* IG = T->isolate_group();
42 ASSERT(IG != NULL);
43
44 SafepointHandler* handler = IG->safepoint_handler();
45 ASSERT(handler != NULL);
46
47 // Signal all threads to get to a safepoint and wait for them to
48 // get to a safepoint.
49 handler->SafepointThreads(T);
50
51 // N.B.: Change growth policy inside the safepoint to prevent racy access.
52 Heap* heap = IG->heap();
53 current_growth_controller_state_ = heap->GrowthControlState();
54 heap->DisableGrowthControl();
55}
56
57ForceGrowthSafepointOperationScope::~ForceGrowthSafepointOperationScope() {
58 Thread* T = thread();
59 ASSERT(T != NULL);
60 IsolateGroup* IG = T->isolate_group();
61 ASSERT(IG != NULL);
62
63 // N.B.: Change growth policy inside the safepoint to prevent racy access.
64 Heap* heap = IG->heap();
65 heap->SetGrowthControlState(current_growth_controller_state_);
66
67 // Resume all threads which are blocked for the safepoint operation.
68 SafepointHandler* handler = IG->safepoint_handler();
69 ASSERT(handler != NULL);
70 handler->ResumeThreads(T);
71
72 if (current_growth_controller_state_) {
73 ASSERT(T->CanCollectGarbage());
74 // Check if we passed the growth limit during the scope.
75 if (heap->old_space()->ReachedHardThreshold()) {
76 heap->CollectGarbage(Heap::kMarkSweep, Heap::kOldSpace);
77 } else {
78 heap->CheckStartConcurrentMarking(T, Heap::kOldSpace);
79 }
80 }
81}
82
83SafepointHandler::SafepointHandler(IsolateGroup* isolate_group)
84 : isolate_group_(isolate_group),
85 safepoint_lock_(),
86 number_threads_not_at_safepoint_(0),
87 safepoint_operation_count_(0),
88 owner_(NULL) {}
89
90SafepointHandler::~SafepointHandler() {
91 ASSERT(owner_ == NULL);
92 ASSERT(safepoint_operation_count_ == 0);
93 isolate_group_ = NULL;
94}
95
96void SafepointHandler::SafepointThreads(Thread* T) {
97 ASSERT(T->no_safepoint_scope_depth() == 0);
98 ASSERT(T->execution_state() == Thread::kThreadInVM);
99
100 {
101 // First grab the threads list lock for this isolate
102 // and check if a safepoint is already in progress. This
103 // ensures that two threads do not start a safepoint operation
104 // at the same time.
105 MonitorLocker sl(threads_lock());
106
107 // Now check to see if a safepoint operation is already in progress
108 // for this isolate, block if an operation is in progress.
109 while (SafepointInProgress()) {
110 // If we are recursively invoking a Safepoint operation then we
111 // just increment the count and return, otherwise we wait for the
112 // safepoint operation to be done.
113 if (owner_ == T) {
114 increment_safepoint_operation_count();
115 return;
116 }
117 sl.WaitWithSafepointCheck(T);
118 }
119
120 // Set safepoint in progress state by this thread.
121 SetSafepointInProgress(T);
122
123 // Go over the active thread list and ensure that all threads active
124 // in the isolate reach a safepoint.
125 Thread* current = isolate_group()->thread_registry()->active_list();
126 while (current != NULL) {
127 MonitorLocker tl(current->thread_lock());
128 if (!current->BypassSafepoints()) {
129 if (current == T) {
130 current->SetAtSafepoint(true);
131 } else {
132 uint32_t state = current->SetSafepointRequested(true);
133 if (!Thread::IsAtSafepoint(state)) {
134 // Thread is not already at a safepoint so try to
135 // get it to a safepoint and wait for it to check in.
136 if (current->IsMutatorThread()) {
137 current->ScheduleInterruptsLocked(Thread::kVMInterrupt);
138 }
139 MonitorLocker sl(&safepoint_lock_);
140 ++number_threads_not_at_safepoint_;
141 }
142 }
143 }
144 current = current->next();
145 }
146 }
147 // Now wait for all threads that are not already at a safepoint to check-in.
148 {
149 MonitorLocker sl(&safepoint_lock_);
150 intptr_t num_attempts = 0;
151 while (number_threads_not_at_safepoint_ > 0) {
152 Monitor::WaitResult retval = sl.Wait(1000);
153 if (retval == Monitor::kTimedOut) {
154 num_attempts += 1;
155 if (FLAG_trace_safepoint && num_attempts > 10) {
156 // We have been waiting too long, start logging this as we might
157 // have an issue where a thread is not checking in for a safepoint.
158 for (Thread* current =
159 isolate_group()->thread_registry()->active_list();
160 current != NULL; current = current->next()) {
161 if (!current->IsAtSafepoint()) {
162 OS::PrintErr("Attempt:%" Pd
163 " waiting for thread %s to check in\n",
164 num_attempts, current->os_thread()->name());
165 }
166 }
167 }
168 }
169 }
170 }
171}
172
173void SafepointHandler::ResumeThreads(Thread* T) {
174 // First resume all the threads which are blocked for the safepoint
175 // operation.
176 MonitorLocker sl(threads_lock());
177
178 // First check if we are in a recursive safepoint operation, in that case
179 // we just decrement safepoint_operation_count and return.
180 ASSERT(SafepointInProgress());
181 if (safepoint_operation_count() > 1) {
182 decrement_safepoint_operation_count();
183 return;
184 }
185 Thread* current = isolate_group()->thread_registry()->active_list();
186 while (current != NULL) {
187 MonitorLocker tl(current->thread_lock());
188 if (!current->BypassSafepoints()) {
189 if (current == T) {
190 current->SetAtSafepoint(false);
191 } else {
192 uint32_t state = current->SetSafepointRequested(false);
193 if (Thread::IsBlockedForSafepoint(state)) {
194 tl.Notify();
195 }
196 }
197 }
198 current = current->next();
199 }
200 // Now reset the safepoint_in_progress_ state and notify all threads
201 // that are waiting to enter the isolate or waiting to start another
202 // safepoint operation.
203 ResetSafepointInProgress(T);
204 sl.NotifyAll();
205}
206
207void SafepointHandler::EnterSafepointUsingLock(Thread* T) {
208 MonitorLocker tl(T->thread_lock());
209 T->SetAtSafepoint(true);
210 if (T->IsSafepointRequested()) {
211 MonitorLocker sl(&safepoint_lock_);
212 ASSERT(number_threads_not_at_safepoint_ > 0);
213 number_threads_not_at_safepoint_ -= 1;
214 sl.Notify();
215 }
216}
217
218void SafepointHandler::ExitSafepointUsingLock(Thread* T) {
219 MonitorLocker tl(T->thread_lock());
220 ASSERT(T->IsAtSafepoint());
221 while (T->IsSafepointRequested()) {
222 T->SetBlockedForSafepoint(true);
223 tl.Wait();
224 T->SetBlockedForSafepoint(false);
225 }
226 T->SetAtSafepoint(false);
227}
228
229void SafepointHandler::BlockForSafepoint(Thread* T) {
230 ASSERT(!T->BypassSafepoints());
231 MonitorLocker tl(T->thread_lock());
232 if (T->IsSafepointRequested()) {
233 T->SetAtSafepoint(true);
234 {
235 MonitorLocker sl(&safepoint_lock_);
236 ASSERT(number_threads_not_at_safepoint_ > 0);
237 number_threads_not_at_safepoint_ -= 1;
238 sl.Notify();
239 }
240 while (T->IsSafepointRequested()) {
241 T->SetBlockedForSafepoint(true);
242 tl.Wait();
243 T->SetBlockedForSafepoint(false);
244 }
245 T->SetAtSafepoint(false);
246 }
247}
248
249} // namespace dart
250