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 17 namespace v8 { 18 namespace internal { 19 WasGCRequested()20bool CollectionBarrier::WasGCRequested() { 21 return collection_requested_.load(); 22 } 23 TryRequestGC()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 37 class BackgroundCollectionInterruptTask : public CancelableTask { 38 public: BackgroundCollectionInterruptTask(Heap * heap)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. RunInternal()50 void RunInternal() override { heap_->CheckCollectionRequested(); } 51 52 Heap* heap_; 53 }; 54 NotifyShutdownRequested()55void CollectionBarrier::NotifyShutdownRequested() { 56 base::MutexGuard guard(&mutex_); 57 if (timer_.IsStarted()) timer_.Stop(); 58 shutdown_requested_ = true; 59 cv_wakeup_.NotifyAll(); 60 } 61 ResumeThreadsAwaitingCollection()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 CancelCollectionAndResumeThreads()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 AwaitCollectionBackground(LocalHeap * local_heap)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 ActivateStackGuardAndPostTask()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 StopTimeToCollectionTimer()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