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