1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/safepoint.h"
6
7 #include <atomic>
8
9 #include "src/base/logging.h"
10 #include "src/base/optional.h"
11 #include "src/base/platform/mutex.h"
12 #include "src/common/globals.h"
13 #include "src/execution/isolate.h"
14 #include "src/handles/handles.h"
15 #include "src/handles/local-handles.h"
16 #include "src/handles/persistent-handles.h"
17 #include "src/heap/gc-tracer-inl.h"
18 #include "src/heap/gc-tracer.h"
19 #include "src/heap/heap-inl.h"
20 #include "src/heap/heap.h"
21 #include "src/heap/local-heap.h"
22 #include "src/heap/parked-scope.h"
23 #include "src/logging/counters-scopes.h"
24 #include "src/objects/objects.h"
25
26 namespace v8 {
27 namespace internal {
28
IsolateSafepoint(Heap * heap)29 IsolateSafepoint::IsolateSafepoint(Heap* heap)
30 : heap_(heap), local_heaps_head_(nullptr), active_safepoint_scopes_(0) {}
31
EnterLocalSafepointScope()32 void IsolateSafepoint::EnterLocalSafepointScope() {
33 // Safepoints need to be initiated on some main thread.
34 DCHECK_NULL(LocalHeap::Current());
35 DCHECK(AllowGarbageCollection::IsAllowed());
36
37 LockMutex(isolate()->main_thread_local_heap());
38 if (++active_safepoint_scopes_ > 1) return;
39
40 // Local safepoint can only be initiated on the isolate's main thread.
41 DCHECK_EQ(ThreadId::Current(), isolate()->thread_id());
42
43 TimedHistogramScope timer(isolate()->counters()->gc_time_to_safepoint());
44 TRACE_GC(heap_->tracer(), GCTracer::Scope::TIME_TO_SAFEPOINT);
45
46 barrier_.Arm();
47 size_t running = SetSafepointRequestedFlags(IncludeMainThread::kNo);
48 barrier_.WaitUntilRunningThreadsInSafepoint(running);
49 }
50
51 class PerClientSafepointData final {
52 public:
PerClientSafepointData(Isolate * isolate)53 explicit PerClientSafepointData(Isolate* isolate) : isolate_(isolate) {}
54
set_locked_and_running(size_t running)55 void set_locked_and_running(size_t running) {
56 locked_ = true;
57 running_ = running;
58 }
59
safepoint() const60 IsolateSafepoint* safepoint() const { return heap()->safepoint(); }
heap() const61 Heap* heap() const { return isolate_->heap(); }
isolate() const62 Isolate* isolate() const { return isolate_; }
63
is_locked() const64 bool is_locked() const { return locked_; }
running() const65 size_t running() const { return running_; }
66
67 private:
68 Isolate* const isolate_;
69 size_t running_ = 0;
70 bool locked_ = false;
71 };
72
InitiateGlobalSafepointScope(Isolate * initiator,PerClientSafepointData * client_data)73 void IsolateSafepoint::InitiateGlobalSafepointScope(
74 Isolate* initiator, PerClientSafepointData* client_data) {
75 shared_isolate()->global_safepoint()->AssertActive();
76 IgnoreLocalGCRequests ignore_gc_requests(initiator->heap());
77 LockMutex(initiator->main_thread_local_heap());
78 InitiateGlobalSafepointScopeRaw(initiator, client_data);
79 }
80
TryInitiateGlobalSafepointScope(Isolate * initiator,PerClientSafepointData * client_data)81 void IsolateSafepoint::TryInitiateGlobalSafepointScope(
82 Isolate* initiator, PerClientSafepointData* client_data) {
83 shared_isolate()->global_safepoint()->AssertActive();
84 if (!local_heaps_mutex_.TryLock()) return;
85 InitiateGlobalSafepointScopeRaw(initiator, client_data);
86 }
87
88 class GlobalSafepointInterruptTask : public CancelableTask {
89 public:
GlobalSafepointInterruptTask(Heap * heap)90 explicit GlobalSafepointInterruptTask(Heap* heap)
91 : CancelableTask(heap->isolate()), heap_(heap) {}
92
93 ~GlobalSafepointInterruptTask() override = default;
94 GlobalSafepointInterruptTask(const GlobalSafepointInterruptTask&) = delete;
95 GlobalSafepointInterruptTask& operator=(const GlobalSafepointInterruptTask&) =
96 delete;
97
98 private:
99 // v8::internal::CancelableTask overrides.
RunInternal()100 void RunInternal() override { heap_->main_thread_local_heap()->Safepoint(); }
101
102 Heap* heap_;
103 };
104
InitiateGlobalSafepointScopeRaw(Isolate * initiator,PerClientSafepointData * client_data)105 void IsolateSafepoint::InitiateGlobalSafepointScopeRaw(
106 Isolate* initiator, PerClientSafepointData* client_data) {
107 CHECK_EQ(++active_safepoint_scopes_, 1);
108 barrier_.Arm();
109
110 size_t running =
111 SetSafepointRequestedFlags(ShouldIncludeMainThread(initiator));
112 client_data->set_locked_and_running(running);
113
114 if (isolate() != initiator) {
115 // An isolate might be waiting in the event loop. Post a task in order to
116 // wake it up.
117 V8::GetCurrentPlatform()
118 ->GetForegroundTaskRunner(reinterpret_cast<v8::Isolate*>(isolate()))
119 ->PostTask(std::make_unique<GlobalSafepointInterruptTask>(heap_));
120 }
121 }
122
ShouldIncludeMainThread(Isolate * initiator)123 IsolateSafepoint::IncludeMainThread IsolateSafepoint::ShouldIncludeMainThread(
124 Isolate* initiator) {
125 const bool is_initiator = isolate() == initiator;
126 return is_initiator ? IncludeMainThread::kNo : IncludeMainThread::kYes;
127 }
128
SetSafepointRequestedFlags(IncludeMainThread include_main_thread)129 size_t IsolateSafepoint::SetSafepointRequestedFlags(
130 IncludeMainThread include_main_thread) {
131 size_t running = 0;
132
133 // There needs to be at least one LocalHeap for the main thread.
134 DCHECK_NOT_NULL(local_heaps_head_);
135
136 for (LocalHeap* local_heap = local_heaps_head_; local_heap;
137 local_heap = local_heap->next_) {
138 if (local_heap->is_main_thread() &&
139 include_main_thread == IncludeMainThread::kNo) {
140 continue;
141 }
142
143 const LocalHeap::ThreadState old_state =
144 local_heap->state_.SetSafepointRequested();
145
146 if (old_state.IsRunning()) running++;
147 CHECK_IMPLIES(old_state.IsCollectionRequested(),
148 local_heap->is_main_thread());
149 CHECK(!old_state.IsSafepointRequested());
150 }
151
152 return running;
153 }
154
LockMutex(LocalHeap * local_heap)155 void IsolateSafepoint::LockMutex(LocalHeap* local_heap) {
156 if (!local_heaps_mutex_.TryLock()) {
157 ParkedScope parked_scope(local_heap);
158 local_heaps_mutex_.Lock();
159 }
160 }
161
LeaveGlobalSafepointScope(Isolate * initiator)162 void IsolateSafepoint::LeaveGlobalSafepointScope(Isolate* initiator) {
163 local_heaps_mutex_.AssertHeld();
164 CHECK_EQ(--active_safepoint_scopes_, 0);
165 ClearSafepointRequestedFlags(ShouldIncludeMainThread(initiator));
166 barrier_.Disarm();
167 local_heaps_mutex_.Unlock();
168 }
169
LeaveLocalSafepointScope()170 void IsolateSafepoint::LeaveLocalSafepointScope() {
171 local_heaps_mutex_.AssertHeld();
172 DCHECK_GT(active_safepoint_scopes_, 0);
173
174 if (--active_safepoint_scopes_ == 0) {
175 ClearSafepointRequestedFlags(IncludeMainThread::kNo);
176 barrier_.Disarm();
177 }
178
179 local_heaps_mutex_.Unlock();
180 }
181
ClearSafepointRequestedFlags(IncludeMainThread include_main_thread)182 void IsolateSafepoint::ClearSafepointRequestedFlags(
183 IncludeMainThread include_main_thread) {
184 for (LocalHeap* local_heap = local_heaps_head_; local_heap;
185 local_heap = local_heap->next_) {
186 if (local_heap->is_main_thread() &&
187 include_main_thread == IncludeMainThread::kNo) {
188 continue;
189 }
190
191 const LocalHeap::ThreadState old_state =
192 local_heap->state_.ClearSafepointRequested();
193
194 CHECK(old_state.IsParked());
195 CHECK(old_state.IsSafepointRequested());
196 CHECK_IMPLIES(old_state.IsCollectionRequested(),
197 local_heap->is_main_thread());
198 }
199 }
200
WaitInSafepoint()201 void IsolateSafepoint::WaitInSafepoint() { barrier_.WaitInSafepoint(); }
202
WaitInUnpark()203 void IsolateSafepoint::WaitInUnpark() { barrier_.WaitInUnpark(); }
204
NotifyPark()205 void IsolateSafepoint::NotifyPark() { barrier_.NotifyPark(); }
206
WaitUntilRunningThreadsInSafepoint(const PerClientSafepointData * client_data)207 void IsolateSafepoint::WaitUntilRunningThreadsInSafepoint(
208 const PerClientSafepointData* client_data) {
209 barrier_.WaitUntilRunningThreadsInSafepoint(client_data->running());
210 }
211
Arm()212 void IsolateSafepoint::Barrier::Arm() {
213 base::MutexGuard guard(&mutex_);
214 DCHECK(!IsArmed());
215 armed_ = true;
216 stopped_ = 0;
217 }
218
Disarm()219 void IsolateSafepoint::Barrier::Disarm() {
220 base::MutexGuard guard(&mutex_);
221 DCHECK(IsArmed());
222 armed_ = false;
223 stopped_ = 0;
224 cv_resume_.NotifyAll();
225 }
226
WaitUntilRunningThreadsInSafepoint(size_t running)227 void IsolateSafepoint::Barrier::WaitUntilRunningThreadsInSafepoint(
228 size_t running) {
229 base::MutexGuard guard(&mutex_);
230 DCHECK(IsArmed());
231 while (stopped_ < running) {
232 cv_stopped_.Wait(&mutex_);
233 }
234 DCHECK_EQ(stopped_, running);
235 }
236
NotifyPark()237 void IsolateSafepoint::Barrier::NotifyPark() {
238 base::MutexGuard guard(&mutex_);
239 CHECK(IsArmed());
240 stopped_++;
241 cv_stopped_.NotifyOne();
242 }
243
WaitInSafepoint()244 void IsolateSafepoint::Barrier::WaitInSafepoint() {
245 base::MutexGuard guard(&mutex_);
246 CHECK(IsArmed());
247 stopped_++;
248 cv_stopped_.NotifyOne();
249
250 while (IsArmed()) {
251 cv_resume_.Wait(&mutex_);
252 }
253 }
254
WaitInUnpark()255 void IsolateSafepoint::Barrier::WaitInUnpark() {
256 base::MutexGuard guard(&mutex_);
257
258 while (IsArmed()) {
259 cv_resume_.Wait(&mutex_);
260 }
261 }
262
Iterate(RootVisitor * visitor)263 void IsolateSafepoint::Iterate(RootVisitor* visitor) {
264 AssertActive();
265 for (LocalHeap* current = local_heaps_head_; current;
266 current = current->next_) {
267 current->handles()->Iterate(visitor);
268 }
269 }
270
AssertMainThreadIsOnlyThread()271 void IsolateSafepoint::AssertMainThreadIsOnlyThread() {
272 DCHECK_EQ(local_heaps_head_, heap_->main_thread_local_heap());
273 DCHECK_NULL(heap_->main_thread_local_heap()->next_);
274 }
275
isolate() const276 Isolate* IsolateSafepoint::isolate() const { return heap_->isolate(); }
277
shared_isolate() const278 Isolate* IsolateSafepoint::shared_isolate() const {
279 return isolate()->shared_isolate();
280 }
281
SafepointScope(Heap * heap)282 SafepointScope::SafepointScope(Heap* heap) : safepoint_(heap->safepoint()) {
283 safepoint_->EnterLocalSafepointScope();
284 }
285
~SafepointScope()286 SafepointScope::~SafepointScope() { safepoint_->LeaveLocalSafepointScope(); }
287
GlobalSafepoint(Isolate * isolate)288 GlobalSafepoint::GlobalSafepoint(Isolate* isolate)
289 : shared_isolate_(isolate), shared_heap_(isolate->heap()) {}
290
AppendClient(Isolate * client)291 void GlobalSafepoint::AppendClient(Isolate* client) {
292 clients_mutex_.AssertHeld();
293
294 DCHECK_NULL(client->global_safepoint_prev_client_isolate_);
295 DCHECK_NULL(client->global_safepoint_next_client_isolate_);
296 DCHECK_NE(clients_head_, client);
297
298 if (clients_head_) {
299 clients_head_->global_safepoint_prev_client_isolate_ = client;
300 }
301
302 client->global_safepoint_prev_client_isolate_ = nullptr;
303 client->global_safepoint_next_client_isolate_ = clients_head_;
304
305 clients_head_ = client;
306 client->shared_isolate_ = shared_isolate_;
307 }
308
RemoveClient(Isolate * client)309 void GlobalSafepoint::RemoveClient(Isolate* client) {
310 DCHECK_EQ(client->heap()->gc_state(), Heap::TEAR_DOWN);
311
312 // A shared heap may have already acquired the client mutex to perform a
313 // shared GC. We need to park the Isolate here to allow for a shared GC.
314 IgnoreLocalGCRequests ignore_gc_requests(client->heap());
315 ParkedMutexGuard guard(client->main_thread_local_heap(), &clients_mutex_);
316
317 if (client->global_safepoint_next_client_isolate_) {
318 client->global_safepoint_next_client_isolate_
319 ->global_safepoint_prev_client_isolate_ =
320 client->global_safepoint_prev_client_isolate_;
321 }
322
323 if (client->global_safepoint_prev_client_isolate_) {
324 client->global_safepoint_prev_client_isolate_
325 ->global_safepoint_next_client_isolate_ =
326 client->global_safepoint_next_client_isolate_;
327 } else {
328 DCHECK_EQ(clients_head_, client);
329 clients_head_ = client->global_safepoint_next_client_isolate_;
330 }
331
332 client->shared_isolate_ = nullptr;
333 }
334
AssertNoClients()335 void GlobalSafepoint::AssertNoClients() { DCHECK_NULL(clients_head_); }
336
EnterGlobalSafepointScope(Isolate * initiator)337 void GlobalSafepoint::EnterGlobalSafepointScope(Isolate* initiator) {
338 // Safepoints need to be initiated on some main thread.
339 DCHECK_NULL(LocalHeap::Current());
340
341 if (!clients_mutex_.TryLock()) {
342 IgnoreLocalGCRequests ignore_gc_requests(initiator->heap());
343 ParkedScope parked_scope(initiator->main_thread_local_heap());
344 clients_mutex_.Lock();
345 }
346
347 TimedHistogramScope timer(
348 initiator->counters()->gc_time_to_global_safepoint());
349 TRACE_GC(initiator->heap()->tracer(),
350 GCTracer::Scope::TIME_TO_GLOBAL_SAFEPOINT);
351
352 std::vector<PerClientSafepointData> clients;
353
354 // Try to initiate safepoint for all clients. Fail immediately when the
355 // local_heaps_mutex_ can't be locked without blocking.
356 IterateClientIsolates([&clients, initiator](Isolate* client) {
357 clients.emplace_back(client);
358 client->heap()->safepoint()->TryInitiateGlobalSafepointScope(
359 initiator, &clients.back());
360 });
361
362 // Make it possible to use AssertActive() on shared isolates.
363 CHECK(shared_isolate_->heap()->safepoint()->local_heaps_mutex_.TryLock());
364
365 // Shared isolates should never have multiple threads.
366 shared_isolate_->heap()->safepoint()->AssertMainThreadIsOnlyThread();
367
368 // Iterate all clients again to initiate the safepoint for all of them - even
369 // if that means blocking.
370 for (PerClientSafepointData& client : clients) {
371 if (client.is_locked()) continue;
372 client.safepoint()->InitiateGlobalSafepointScope(initiator, &client);
373 }
374
375 #if DEBUG
376 for (const PerClientSafepointData& client : clients) {
377 DCHECK_EQ(client.isolate()->shared_isolate(), shared_isolate_);
378 DCHECK(client.heap()->deserialization_complete());
379 }
380 #endif // DEBUG
381
382 // Now that safepoints were initiated for all clients, wait until all threads
383 // of all clients reached a safepoint.
384 for (const PerClientSafepointData& client : clients) {
385 DCHECK(client.is_locked());
386 client.safepoint()->WaitUntilRunningThreadsInSafepoint(&client);
387 }
388 }
389
LeaveGlobalSafepointScope(Isolate * initiator)390 void GlobalSafepoint::LeaveGlobalSafepointScope(Isolate* initiator) {
391 shared_isolate_->heap()->safepoint()->local_heaps_mutex_.Unlock();
392
393 IterateClientIsolates([initiator](Isolate* client) {
394 Heap* client_heap = client->heap();
395 client_heap->safepoint()->LeaveGlobalSafepointScope(initiator);
396 });
397
398 clients_mutex_.Unlock();
399 }
400
GlobalSafepointScope(Isolate * initiator)401 GlobalSafepointScope::GlobalSafepointScope(Isolate* initiator)
402 : initiator_(initiator), shared_isolate_(initiator->shared_isolate()) {
403 if (shared_isolate_) {
404 shared_isolate_->global_safepoint()->EnterGlobalSafepointScope(initiator_);
405 } else {
406 initiator_->heap()->safepoint()->EnterLocalSafepointScope();
407 }
408 }
409
~GlobalSafepointScope()410 GlobalSafepointScope::~GlobalSafepointScope() {
411 if (shared_isolate_) {
412 shared_isolate_->global_safepoint()->LeaveGlobalSafepointScope(initiator_);
413 } else {
414 initiator_->heap()->safepoint()->LeaveLocalSafepointScope();
415 }
416 }
417
418 } // namespace internal
419 } // namespace v8
420