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 | |
11 | namespace dart { |
12 | |
13 | DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic." ); |
14 | |
15 | SafepointOperationScope::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 | |
27 | SafepointOperationScope::~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 | |
37 | ForceGrowthSafepointOperationScope::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 | |
57 | ForceGrowthSafepointOperationScope::~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 | |
83 | SafepointHandler::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 | |
90 | SafepointHandler::~SafepointHandler() { |
91 | ASSERT(owner_ == NULL); |
92 | ASSERT(safepoint_operation_count_ == 0); |
93 | isolate_group_ = NULL; |
94 | } |
95 | |
96 | void 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 | |
173 | void 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 | |
207 | void 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 | |
218 | void 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 | |
229 | void 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 | |