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