| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/heap/collection-barrier.h" |
| |
| #include "src/base/platform/mutex.h" |
| #include "src/base/platform/time.h" |
| #include "src/common/globals.h" |
| #include "src/execution/isolate.h" |
| #include "src/handles/handles.h" |
| #include "src/heap/gc-tracer.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/heap.h" |
| #include "src/heap/local-heap.h" |
| #include "src/heap/parked-scope.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| bool CollectionBarrier::WasGCRequested() { |
| return collection_requested_.load(); |
| } |
| |
| bool CollectionBarrier::TryRequestGC() { |
| base::MutexGuard guard(&mutex_); |
| if (shutdown_requested_) return false; |
| bool was_already_requested = collection_requested_.exchange(true); |
| |
| if (!was_already_requested) { |
| CHECK(!timer_.IsStarted()); |
| timer_.Start(); |
| } |
| |
| return true; |
| } |
| |
| class BackgroundCollectionInterruptTask : public CancelableTask { |
| public: |
| explicit BackgroundCollectionInterruptTask(Heap* heap) |
| : CancelableTask(heap->isolate()), heap_(heap) {} |
| |
| ~BackgroundCollectionInterruptTask() override = default; |
| BackgroundCollectionInterruptTask(const BackgroundCollectionInterruptTask&) = |
| delete; |
| BackgroundCollectionInterruptTask& operator=( |
| const BackgroundCollectionInterruptTask&) = delete; |
| |
| private: |
| // v8::internal::CancelableTask overrides. |
| void RunInternal() override { heap_->CheckCollectionRequested(); } |
| |
| Heap* heap_; |
| }; |
| |
| void CollectionBarrier::NotifyShutdownRequested() { |
| base::MutexGuard guard(&mutex_); |
| if (timer_.IsStarted()) timer_.Stop(); |
| shutdown_requested_ = true; |
| cv_wakeup_.NotifyAll(); |
| } |
| |
| void CollectionBarrier::ResumeThreadsAwaitingCollection() { |
| base::MutexGuard guard(&mutex_); |
| DCHECK(!timer_.IsStarted()); |
| collection_requested_.store(false); |
| block_for_collection_ = false; |
| collection_performed_ = true; |
| cv_wakeup_.NotifyAll(); |
| } |
| |
| void CollectionBarrier::CancelCollectionAndResumeThreads() { |
| base::MutexGuard guard(&mutex_); |
| if (timer_.IsStarted()) timer_.Stop(); |
| collection_requested_.store(false); |
| block_for_collection_ = false; |
| collection_performed_ = false; |
| cv_wakeup_.NotifyAll(); |
| } |
| |
| bool CollectionBarrier::AwaitCollectionBackground(LocalHeap* local_heap) { |
| bool first_thread; |
| |
| { |
| // Update flag before parking this thread, this guarantees that the flag is |
| // set before the next GC. |
| base::MutexGuard guard(&mutex_); |
| if (shutdown_requested_) return false; |
| |
| // Collection was cancelled by the main thread. |
| if (!collection_requested_.load()) return false; |
| |
| first_thread = !block_for_collection_; |
| block_for_collection_ = true; |
| CHECK(timer_.IsStarted()); |
| } |
| |
| // The first thread needs to activate the stack guard and post the task. |
| if (first_thread) ActivateStackGuardAndPostTask(); |
| |
| ParkedScope scope(local_heap); |
| base::MutexGuard guard(&mutex_); |
| |
| while (block_for_collection_) { |
| if (shutdown_requested_) return false; |
| cv_wakeup_.Wait(&mutex_); |
| } |
| |
| // Collection may have been cancelled while blocking for it. |
| return collection_performed_; |
| } |
| |
| void CollectionBarrier::ActivateStackGuardAndPostTask() { |
| Isolate* isolate = heap_->isolate(); |
| ExecutionAccess access(isolate); |
| isolate->stack_guard()->RequestGC(); |
| |
| V8::GetCurrentPlatform() |
| ->GetForegroundTaskRunner(reinterpret_cast<v8::Isolate*>(isolate)) |
| ->PostTask(std::make_unique<BackgroundCollectionInterruptTask>(heap_)); |
| } |
| |
| void CollectionBarrier::StopTimeToCollectionTimer() { |
| if (collection_requested_.load()) { |
| base::MutexGuard guard(&mutex_); |
| // The first thread that requests the GC, starts the timer first and *then* |
| // parks itself. Since we are in a safepoint here, the timer is always |
| // initialized here already. |
| CHECK(timer_.IsStarted()); |
| base::TimeDelta delta = timer_.Elapsed(); |
| TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.gc"), |
| "V8.GC.TimeToCollectionOnBackground", |
| TRACE_EVENT_SCOPE_THREAD, "duration", |
| delta.InMillisecondsF()); |
| heap_->isolate() |
| ->counters() |
| ->gc_time_to_collection_on_background() |
| ->AddTimedSample(delta); |
| timer_.Stop(); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |