• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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