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/cppgc/gc-invoker.h" 6 7#include <memory> 8 9#include "include/cppgc/common.h" 10#include "include/cppgc/platform.h" 11#include "src/heap/cppgc/heap.h" 12#include "src/heap/cppgc/task-handle.h" 13 14namespace cppgc { 15namespace internal { 16 17class GCInvoker::GCInvokerImpl final : public GarbageCollector { 18 public: 19 GCInvokerImpl(GarbageCollector*, cppgc::Platform*, cppgc::Heap::StackSupport); 20 ~GCInvokerImpl(); 21 22 GCInvokerImpl(const GCInvokerImpl&) = delete; 23 GCInvokerImpl& operator=(const GCInvokerImpl&) = delete; 24 25 void CollectGarbage(GarbageCollector::Config) final; 26 void StartIncrementalGarbageCollection(GarbageCollector::Config) final; 27 size_t epoch() const final { return collector_->epoch(); } 28 const EmbedderStackState* override_stack_state() const final { 29 return collector_->override_stack_state(); 30 } 31 32 private: 33 class GCTask final : public cppgc::Task { 34 public: 35 using Handle = SingleThreadedHandle; 36 37 static Handle Post(GarbageCollector* collector, cppgc::TaskRunner* runner, 38 GarbageCollector::Config config) { 39 auto task = 40 std::make_unique<GCInvoker::GCInvokerImpl::GCTask>(collector, config); 41 auto handle = task->GetHandle(); 42 runner->PostNonNestableTask(std::move(task)); 43 return handle; 44 } 45 46 explicit GCTask(GarbageCollector* collector, 47 GarbageCollector::Config config) 48 : collector_(collector), 49 config_(config), 50 handle_(Handle::NonEmptyTag{}), 51 saved_epoch_(collector->epoch()) {} 52 53 private: 54 void Run() final { 55 CHECK_NULL(collector_->override_stack_state()); 56 57 if (handle_.IsCanceled() || (collector_->epoch() != saved_epoch_)) return; 58 59 collector_->CollectGarbage(config_); 60 handle_.Cancel(); 61 } 62 63 Handle GetHandle() { return handle_; } 64 65 GarbageCollector* collector_; 66 GarbageCollector::Config config_; 67 Handle handle_; 68 size_t saved_epoch_; 69 }; 70 71 GarbageCollector* collector_; 72 cppgc::Platform* platform_; 73 cppgc::Heap::StackSupport stack_support_; 74 GCTask::Handle gc_task_handle_; 75}; 76 77GCInvoker::GCInvokerImpl::GCInvokerImpl(GarbageCollector* collector, 78 cppgc::Platform* platform, 79 cppgc::Heap::StackSupport stack_support) 80 : collector_(collector), 81 platform_(platform), 82 stack_support_(stack_support) {} 83 84GCInvoker::GCInvokerImpl::~GCInvokerImpl() { 85 if (gc_task_handle_) { 86 gc_task_handle_.Cancel(); 87 } 88} 89 90void GCInvoker::GCInvokerImpl::CollectGarbage(GarbageCollector::Config config) { 91 DCHECK_EQ(config.marking_type, cppgc::Heap::MarkingType::kAtomic); 92 if ((config.stack_state == 93 GarbageCollector::Config::StackState::kNoHeapPointers) || 94 (stack_support_ == 95 cppgc::Heap::StackSupport::kSupportsConservativeStackScan)) { 96 collector_->CollectGarbage(config); 97 } else if (platform_->GetForegroundTaskRunner() && 98 platform_->GetForegroundTaskRunner()->NonNestableTasksEnabled()) { 99 if (!gc_task_handle_) { 100 // Force a precise GC since it will run in a non-nestable task. 101 config.stack_state = 102 GarbageCollector::Config::StackState::kNoHeapPointers; 103 DCHECK_NE(cppgc::Heap::StackSupport::kSupportsConservativeStackScan, 104 stack_support_); 105 gc_task_handle_ = GCTask::Post( 106 collector_, platform_->GetForegroundTaskRunner().get(), config); 107 } 108 } 109} 110 111void GCInvoker::GCInvokerImpl::StartIncrementalGarbageCollection( 112 GarbageCollector::Config config) { 113 DCHECK_NE(config.marking_type, cppgc::Heap::MarkingType::kAtomic); 114 if ((stack_support_ != 115 cppgc::Heap::StackSupport::kSupportsConservativeStackScan) && 116 (!platform_->GetForegroundTaskRunner() || 117 !platform_->GetForegroundTaskRunner()->NonNestableTasksEnabled())) { 118 // In this configuration the GC finalization can only be triggered through 119 // ForceGarbageCollectionSlow. If incremental GC is started, there is no 120 // way to know how long it will remain enabled (and the write barrier with 121 // it). For that reason, we do not support running incremental GCs in this 122 // configuration. 123 return; 124 } 125 // No need to postpone starting incremental GC since the stack is not scanned 126 // until GC finalization. 127 collector_->StartIncrementalGarbageCollection(config); 128} 129 130GCInvoker::GCInvoker(GarbageCollector* collector, cppgc::Platform* platform, 131 cppgc::Heap::StackSupport stack_support) 132 : impl_(std::make_unique<GCInvoker::GCInvokerImpl>(collector, platform, 133 stack_support)) {} 134 135GCInvoker::~GCInvoker() = default; 136 137void GCInvoker::CollectGarbage(GarbageCollector::Config config) { 138 impl_->CollectGarbage(config); 139} 140 141void GCInvoker::StartIncrementalGarbageCollection( 142 GarbageCollector::Config config) { 143 impl_->StartIncrementalGarbageCollection(config); 144} 145 146size_t GCInvoker::epoch() const { return impl_->epoch(); } 147 148const EmbedderStackState* GCInvoker::override_stack_state() const { 149 return impl_->override_stack_state(); 150} 151 152} // namespace internal 153} // namespace cppgc 154