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/heap.h"
6
7 #include "include/cppgc/heap-consistency.h"
8 #include "src/heap/base/stack.h"
9 #include "src/heap/cppgc/garbage-collector.h"
10 #include "src/heap/cppgc/gc-invoker.h"
11 #include "src/heap/cppgc/heap-object-header.h"
12 #include "src/heap/cppgc/heap-visitor.h"
13 #include "src/heap/cppgc/marker.h"
14 #include "src/heap/cppgc/marking-verifier.h"
15 #include "src/heap/cppgc/prefinalizer-handler.h"
16 #include "src/heap/cppgc/stats-collector.h"
17 #include "src/heap/cppgc/sweeper.h"
18 #include "src/heap/cppgc/unmarker.h"
19
20 namespace cppgc {
21
22 namespace {
23
VerifyCustomSpaces(const std::vector<std::unique_ptr<CustomSpaceBase>> & custom_spaces)24 void VerifyCustomSpaces(
25 const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces) {
26 // Ensures that user-provided custom spaces have indices that form a sequence
27 // starting at 0.
28 #ifdef DEBUG
29 for (size_t i = 0; i < custom_spaces.size(); ++i) {
30 DCHECK_EQ(i, custom_spaces[i]->GetCustomSpaceIndex().value);
31 }
32 #endif // DEBUG
33 }
34
35 } // namespace
36
Create(std::shared_ptr<cppgc::Platform> platform,cppgc::Heap::HeapOptions options)37 std::unique_ptr<Heap> Heap::Create(std::shared_ptr<cppgc::Platform> platform,
38 cppgc::Heap::HeapOptions options) {
39 DCHECK(platform.get());
40 VerifyCustomSpaces(options.custom_spaces);
41 return std::make_unique<internal::Heap>(std::move(platform),
42 std::move(options));
43 }
44
ForceGarbageCollectionSlow(const char * source,const char * reason,Heap::StackState stack_state)45 void Heap::ForceGarbageCollectionSlow(const char* source, const char* reason,
46 Heap::StackState stack_state) {
47 internal::Heap::From(this)->CollectGarbage(
48 {internal::GarbageCollector::Config::CollectionType::kMajor, stack_state,
49 MarkingType::kAtomic, SweepingType::kAtomic,
50 internal::GarbageCollector::Config::FreeMemoryHandling::
51 kDiscardWherePossible,
52 internal::GarbageCollector::Config::IsForcedGC::kForced});
53 }
54
GetAllocationHandle()55 AllocationHandle& Heap::GetAllocationHandle() {
56 return internal::Heap::From(this)->object_allocator();
57 }
58
GetHeapHandle()59 HeapHandle& Heap::GetHeapHandle() { return *internal::Heap::From(this); }
60
61 namespace internal {
62
63 namespace {
64
CheckConfig(Heap::Config config,HeapBase::MarkingType marking_support,HeapBase::SweepingType sweeping_support)65 void CheckConfig(Heap::Config config, HeapBase::MarkingType marking_support,
66 HeapBase::SweepingType sweeping_support) {
67 CHECK_WITH_MSG(
68 (config.collection_type != Heap::Config::CollectionType::kMinor) ||
69 (config.stack_state == Heap::Config::StackState::kNoHeapPointers),
70 "Minor GCs with stack is currently not supported");
71 CHECK_LE(static_cast<int>(config.marking_type),
72 static_cast<int>(marking_support));
73 CHECK_LE(static_cast<int>(config.sweeping_type),
74 static_cast<int>(sweeping_support));
75 }
76
77 } // namespace
78
Heap(std::shared_ptr<cppgc::Platform> platform,cppgc::Heap::HeapOptions options)79 Heap::Heap(std::shared_ptr<cppgc::Platform> platform,
80 cppgc::Heap::HeapOptions options)
81 : HeapBase(platform, options.custom_spaces, options.stack_support,
82 options.marking_support, options.sweeping_support),
83 gc_invoker_(this, platform_.get(), options.stack_support),
84 growing_(&gc_invoker_, stats_collector_.get(),
85 options.resource_constraints, options.marking_support,
86 options.sweeping_support) {
87 CHECK_IMPLIES(options.marking_support != HeapBase::MarkingType::kAtomic,
88 platform_->GetForegroundTaskRunner());
89 CHECK_IMPLIES(options.sweeping_support != HeapBase::SweepingType::kAtomic,
90 platform_->GetForegroundTaskRunner());
91 }
92
~Heap()93 Heap::~Heap() {
94 // Gracefully finish already running GC if any, but don't finalize live
95 // objects.
96 FinalizeIncrementalGarbageCollectionIfRunning(
97 {Config::CollectionType::kMajor,
98 Config::StackState::kMayContainHeapPointers,
99 Config::MarkingType::kAtomic, Config::SweepingType::kAtomic});
100 {
101 subtle::NoGarbageCollectionScope no_gc(*this);
102 sweeper_.FinishIfRunning();
103 }
104 }
105
CollectGarbage(Config config)106 void Heap::CollectGarbage(Config config) {
107 DCHECK_EQ(Config::MarkingType::kAtomic, config.marking_type);
108 CheckConfig(config, marking_support_, sweeping_support_);
109
110 if (in_no_gc_scope()) return;
111
112 config_ = config;
113
114 if (!IsMarking()) {
115 StartGarbageCollection(config);
116 }
117 DCHECK(IsMarking());
118 FinalizeGarbageCollection(config.stack_state);
119 }
120
StartIncrementalGarbageCollection(Config config)121 void Heap::StartIncrementalGarbageCollection(Config config) {
122 DCHECK_NE(Config::MarkingType::kAtomic, config.marking_type);
123 DCHECK_NE(marking_support_, Config::MarkingType::kAtomic);
124 CheckConfig(config, marking_support_, sweeping_support_);
125
126 if (IsMarking() || in_no_gc_scope()) return;
127
128 config_ = config;
129
130 StartGarbageCollection(config);
131 }
132
FinalizeIncrementalGarbageCollectionIfRunning(Config config)133 void Heap::FinalizeIncrementalGarbageCollectionIfRunning(Config config) {
134 CheckConfig(config, marking_support_, sweeping_support_);
135
136 if (!IsMarking()) return;
137
138 DCHECK(!in_no_gc_scope());
139
140 DCHECK_NE(Config::MarkingType::kAtomic, config_.marking_type);
141 config_ = config;
142 FinalizeGarbageCollection(config.stack_state);
143 }
144
StartGarbageCollection(Config config)145 void Heap::StartGarbageCollection(Config config) {
146 DCHECK(!IsMarking());
147 DCHECK(!in_no_gc_scope());
148
149 // Finish sweeping in case it is still running.
150 sweeper_.FinishIfRunning();
151
152 epoch_++;
153
154 #if defined(CPPGC_YOUNG_GENERATION)
155 if (config.collection_type == Config::CollectionType::kMajor)
156 SequentialUnmarker unmarker(raw_heap());
157 #endif // defined(CPPGC_YOUNG_GENERATION)
158
159 const Marker::MarkingConfig marking_config{
160 config.collection_type, config.stack_state, config.marking_type,
161 config.is_forced_gc};
162 marker_ = std::make_unique<Marker>(AsBase(), platform_.get(), marking_config);
163 marker_->StartMarking();
164 }
165
FinalizeGarbageCollection(Config::StackState stack_state)166 void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
167 DCHECK(IsMarking());
168 DCHECK(!in_no_gc_scope());
169 CHECK(!in_disallow_gc_scope());
170 config_.stack_state = stack_state;
171 SetStackEndOfCurrentGC(v8::base::Stack::GetCurrentStackPosition());
172 in_atomic_pause_ = true;
173 {
174 // This guards atomic pause marking, meaning that no internal method or
175 // external callbacks are allowed to allocate new objects.
176 cppgc::subtle::DisallowGarbageCollectionScope no_gc_scope(*this);
177 marker_->FinishMarking(config_.stack_state);
178 }
179 marker_.reset();
180 const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers();
181 #if CPPGC_VERIFY_HEAP
182 MarkingVerifier verifier(*this, config_.collection_type);
183 verifier.Run(config_.stack_state, stack_end_of_current_gc(),
184 stats_collector()->marked_bytes_on_current_cycle() +
185 bytes_allocated_in_prefinalizers);
186 #endif // CPPGC_VERIFY_HEAP
187 #ifndef CPPGC_ALLOW_ALLOCATIONS_IN_PREFINALIZERS
188 DCHECK_EQ(0u, bytes_allocated_in_prefinalizers);
189 #endif
190 USE(bytes_allocated_in_prefinalizers);
191
192 #if defined(CPPGC_YOUNG_GENERATION)
193 ResetRememberedSet();
194 #endif // defined(CPPGC_YOUNG_GENERATION)
195
196 subtle::NoGarbageCollectionScope no_gc(*this);
197 const Sweeper::SweepingConfig sweeping_config{
198 config_.sweeping_type,
199 Sweeper::SweepingConfig::CompactableSpaceHandling::kSweep,
200 config_.free_memory_handling};
201 sweeper_.Start(sweeping_config);
202 in_atomic_pause_ = false;
203 sweeper_.NotifyDoneIfNeeded();
204 }
205
DisableHeapGrowingForTesting()206 void Heap::DisableHeapGrowingForTesting() { growing_.DisableForTesting(); }
207
FinalizeIncrementalGarbageCollectionIfNeeded(Config::StackState stack_state)208 void Heap::FinalizeIncrementalGarbageCollectionIfNeeded(
209 Config::StackState stack_state) {
210 StatsCollector::EnabledScope stats_scope(
211 stats_collector(), StatsCollector::kMarkIncrementalFinalize);
212 FinalizeGarbageCollection(stack_state);
213 }
214
StartIncrementalGarbageCollectionForTesting()215 void Heap::StartIncrementalGarbageCollectionForTesting() {
216 DCHECK(!IsMarking());
217 DCHECK(!in_no_gc_scope());
218 StartGarbageCollection({Config::CollectionType::kMajor,
219 Config::StackState::kNoHeapPointers,
220 Config::MarkingType::kIncrementalAndConcurrent,
221 Config::SweepingType::kIncrementalAndConcurrent});
222 }
223
FinalizeIncrementalGarbageCollectionForTesting(EmbedderStackState stack_state)224 void Heap::FinalizeIncrementalGarbageCollectionForTesting(
225 EmbedderStackState stack_state) {
226 DCHECK(!in_no_gc_scope());
227 DCHECK(IsMarking());
228 FinalizeGarbageCollection(stack_state);
229 sweeper_.FinishIfRunning();
230 }
231
232 } // namespace internal
233 } // namespace cppgc
234