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/collection-barrier.h"
6 
7 #include "src/base/platform/mutex.h"
8 #include "src/base/platform/time.h"
9 #include "src/common/globals.h"
10 #include "src/execution/isolate.h"
11 #include "src/handles/handles.h"
12 #include "src/heap/gc-tracer.h"
13 #include "src/heap/heap-inl.h"
14 #include "src/heap/heap.h"
15 #include "src/heap/local-heap.h"
16 #include "src/heap/parked-scope.h"
17 
18 namespace v8 {
19 namespace internal {
20 
WasGCRequested()21 bool CollectionBarrier::WasGCRequested() {
22   return collection_requested_.load();
23 }
24 
RequestGC()25 void CollectionBarrier::RequestGC() {
26   base::MutexGuard guard(&mutex_);
27   bool was_already_requested = collection_requested_.exchange(true);
28 
29   if (!was_already_requested) {
30     CHECK(!timer_.IsStarted());
31     timer_.Start();
32   }
33 }
34 
35 class BackgroundCollectionInterruptTask : public CancelableTask {
36  public:
BackgroundCollectionInterruptTask(Heap * heap)37   explicit BackgroundCollectionInterruptTask(Heap* heap)
38       : CancelableTask(heap->isolate()), heap_(heap) {}
39 
40   ~BackgroundCollectionInterruptTask() override = default;
41   BackgroundCollectionInterruptTask(const BackgroundCollectionInterruptTask&) =
42       delete;
43   BackgroundCollectionInterruptTask& operator=(
44       const BackgroundCollectionInterruptTask&) = delete;
45 
46  private:
47   // v8::internal::CancelableTask overrides.
RunInternal()48   void RunInternal() override { heap_->CheckCollectionRequested(); }
49 
50   Heap* heap_;
51 };
52 
NotifyShutdownRequested()53 void CollectionBarrier::NotifyShutdownRequested() {
54   base::MutexGuard guard(&mutex_);
55   if (timer_.IsStarted()) timer_.Stop();
56   shutdown_requested_ = true;
57   cv_wakeup_.NotifyAll();
58 }
59 
ResumeThreadsAwaitingCollection()60 void CollectionBarrier::ResumeThreadsAwaitingCollection() {
61   base::MutexGuard guard(&mutex_);
62   collection_requested_.store(false);
63   block_for_collection_ = false;
64   cv_wakeup_.NotifyAll();
65 }
66 
AwaitCollectionBackground(LocalHeap * local_heap)67 bool CollectionBarrier::AwaitCollectionBackground(LocalHeap* local_heap) {
68   bool first_thread;
69 
70   {
71     // Update flag before parking this thread, this guarantees that the flag is
72     // set before the next GC.
73     base::MutexGuard guard(&mutex_);
74     if (shutdown_requested_) return false;
75     first_thread = !block_for_collection_;
76     block_for_collection_ = true;
77     CHECK(timer_.IsStarted());
78   }
79 
80   // The first thread needs to activate the stack guard and post the task.
81   if (first_thread) ActivateStackGuardAndPostTask();
82 
83   ParkedScope scope(local_heap);
84   base::MutexGuard guard(&mutex_);
85 
86   while (block_for_collection_) {
87     if (shutdown_requested_) return false;
88     cv_wakeup_.Wait(&mutex_);
89   }
90 
91   return true;
92 }
93 
ActivateStackGuardAndPostTask()94 void CollectionBarrier::ActivateStackGuardAndPostTask() {
95   Isolate* isolate = heap_->isolate();
96   ExecutionAccess access(isolate);
97   isolate->stack_guard()->RequestGC();
98 
99   V8::GetCurrentPlatform()
100       ->GetForegroundTaskRunner(reinterpret_cast<v8::Isolate*>(isolate))
101       ->PostTask(std::make_unique<BackgroundCollectionInterruptTask>(heap_));
102 }
103 
StopTimeToCollectionTimer()104 void CollectionBarrier::StopTimeToCollectionTimer() {
105   if (collection_requested_.load()) {
106     base::MutexGuard guard(&mutex_);
107     // The first thread that requests the GC, starts the timer first and *then*
108     // parks itself. Since we are in a safepoint here, the timer is always
109     // initialized here already.
110     CHECK(timer_.IsStarted());
111     base::TimeDelta delta = timer_.Elapsed();
112     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.gc"),
113                          "V8.GC.TimeToCollectionOnBackground",
114                          TRACE_EVENT_SCOPE_THREAD, "duration",
115                          delta.InMillisecondsF());
116     heap_->isolate()
117         ->counters()
118         ->gc_time_to_collection_on_background()
119         ->AddTimedSample(delta);
120     timer_.Stop();
121   }
122 }
123 
124 }  // namespace internal
125 }  // namespace v8
126