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
14 namespace cppgc {
15 namespace internal {
16
17 class 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;
epoch() const27 size_t epoch() const final { return collector_->epoch(); }
override_stack_state() const28 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
Post(GarbageCollector * collector,cppgc::TaskRunner * runner,GarbageCollector::Config config)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
GCTask(GarbageCollector * collector,GarbageCollector::Config config)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:
Run()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
GetHandle()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
GCInvokerImpl(GarbageCollector * collector,cppgc::Platform * platform,cppgc::Heap::StackSupport stack_support)77 GCInvoker::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
~GCInvokerImpl()84 GCInvoker::GCInvokerImpl::~GCInvokerImpl() {
85 if (gc_task_handle_) {
86 gc_task_handle_.Cancel();
87 }
88 }
89
CollectGarbage(GarbageCollector::Config config)90 void 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
StartIncrementalGarbageCollection(GarbageCollector::Config config)111 void 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
GCInvoker(GarbageCollector * collector,cppgc::Platform * platform,cppgc::Heap::StackSupport stack_support)130 GCInvoker::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
135 GCInvoker::~GCInvoker() = default;
136
CollectGarbage(GarbageCollector::Config config)137 void GCInvoker::CollectGarbage(GarbageCollector::Config config) {
138 impl_->CollectGarbage(config);
139 }
140
StartIncrementalGarbageCollection(GarbageCollector::Config config)141 void GCInvoker::StartIncrementalGarbageCollection(
142 GarbageCollector::Config config) {
143 impl_->StartIncrementalGarbageCollection(config);
144 }
145
epoch() const146 size_t GCInvoker::epoch() const { return impl_->epoch(); }
147
override_stack_state() const148 const EmbedderStackState* GCInvoker::override_stack_state() const {
149 return impl_->override_stack_state();
150 }
151
152 } // namespace internal
153 } // namespace cppgc
154