Commit 3cb46341 authored by Anton Bikineev's avatar Anton Bikineev Committed by V8 LUCI CQ

cppgc: young-gen: Use ref-counting scheme to enable the barrier

The CL uses the different scheme to enable the generational barrier. The
separate global counter (is_enabled_) keeps track of the number of heaps
that enable generational GC. If at least one of the heaps enables the
generational GC, the counter will enable the write barrier. Technically,
the counter could be merged with WriteBarrier::is_enabled_, but having a
separate variable allows us to keep DCHECKs if generational barrier is
enabled.

Bug: chromium:1029379
Change-Id: Iafaa76f96acb18a73f8bde7231434e68c04cb683
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3616518Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80281}
parent 671dcfac
......@@ -663,10 +663,9 @@ void CppHeap::TraceEpilogue() {
#if defined(CPPGC_YOUNG_GENERATION)
// Check if the young generation was enabled via flag.
if (FLAG_cppgc_young_generation)
cppgc::internal::YoungGenerationEnabler::Enable();
if (FLAG_cppgc_young_generation) EnableGenerationalGC();
ResetRememberedSetAndEnableMinorGCIfNeeded();
ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
{
......
......@@ -122,7 +122,16 @@ size_t HeapBase::ExecutePreFinalizers() {
}
#if defined(CPPGC_YOUNG_GENERATION)
void HeapBase::ResetRememberedSetAndEnableMinorGCIfNeeded() {
void HeapBase::EnableGenerationalGC() {
DCHECK(in_atomic_pause());
// Notify the global flag that the write barrier must always be enabled.
YoungGenerationEnabler::Enable();
// Enable young generation for the current heap.
caged_heap().EnableGenerationalGC();
generation_support_ = GenerationSupport::kYoungAndOldGenerations;
}
void HeapBase::ResetRememberedSet() {
DCHECK(in_atomic_pause());
class AllLABsAreEmpty final : protected HeapVisitor<AllLABsAreEmpty> {
friend class HeapVisitor<AllLABsAreEmpty>;
......@@ -144,27 +153,14 @@ void HeapBase::ResetRememberedSetAndEnableMinorGCIfNeeded() {
};
DCHECK(AllLABsAreEmpty(raw_heap()).value());
if (generational_gc_supported()) {
caged_heap().local_data().age_table.Reset(&caged_heap().allocator());
remembered_set_.Reset();
if (!generational_gc_supported()) {
DCHECK(remembered_set_.IsEmpty());
return;
}
DCHECK(remembered_set_.IsEmpty());
// Check if the young generation was enabled since the last cycle.
if (YoungGenerationEnabler::IsEnabled()) {
// Enable young generation for the current heap.
caged_heap().EnableGenerationalGC();
generation_support_ = GenerationSupport::kYoungAndOldGenerations;
}
}
void HeapBase::DisableGenerationalGCForTesting() {
DCHECK(caged_heap().local_data().is_young_generation_enabled);
DCHECK_EQ(GenerationSupport::kYoungAndOldGenerations, generation_support_);
caged_heap().local_data().is_young_generation_enabled = false;
generation_support_ = GenerationSupport::kSingleGeneration;
caged_heap().local_data().age_table.Reset(&caged_heap().allocator());
remembered_set_.Reset();
return;
}
#endif // defined(CPPGC_YOUNG_GENERATION)
......@@ -175,6 +171,17 @@ void HeapBase::Terminate() {
sweeper().FinishIfRunning();
#if defined(CPPGC_YOUNG_GENERATION)
if (generational_gc_supported()) {
DCHECK(caged_heap().local_data().is_young_generation_enabled);
DCHECK_EQ(GenerationSupport::kYoungAndOldGenerations, generation_support_);
caged_heap().local_data().is_young_generation_enabled = false;
generation_support_ = GenerationSupport::kSingleGeneration;
YoungGenerationEnabler::Disable();
}
#endif // defined(CPPGC_YOUNG_GENERATION)
constexpr size_t kMaxTerminationGCs = 20;
size_t gc_count = 0;
bool more_termination_gcs_needed = false;
......
......@@ -224,10 +224,6 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
return supported;
}
#if defined(CPPGC_YOUNG_GENERATION)
void DisableGenerationalGCForTesting();
#endif // defined(CPPGC_YOUNG_GENERATION)
protected:
enum class GenerationSupport : uint8_t {
kSingleGeneration,
......@@ -246,7 +242,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
size_t ExecutePreFinalizers();
#if defined(CPPGC_YOUNG_GENERATION)
void ResetRememberedSetAndEnableMinorGCIfNeeded();
void EnableGenerationalGC();
void ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
PageAllocator* page_allocator() const;
......
......@@ -190,7 +190,10 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
USE(bytes_allocated_in_prefinalizers);
#if defined(CPPGC_YOUNG_GENERATION)
ResetRememberedSetAndEnableMinorGCIfNeeded();
if (generational_gc_enabled_) {
HeapBase::EnableGenerationalGC();
}
ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)
subtle::NoGarbageCollectionScope no_gc(*this);
......@@ -203,6 +206,12 @@ void Heap::FinalizeGarbageCollection(Config::StackState stack_state) {
sweeper_.NotifyDoneIfNeeded();
}
void Heap::EnableGenerationalGC() {
DCHECK(!IsMarking());
DCHECK(!generational_gc_enabled_);
generational_gc_enabled_ = true;
}
void Heap::DisableHeapGrowingForTesting() { growing_.DisableForTesting(); }
void Heap::FinalizeIncrementalGarbageCollectionIfNeeded(
......
......@@ -41,6 +41,8 @@ class V8_EXPORT_PRIVATE Heap final : public HeapBase,
return HeapBase::override_stack_state();
}
void EnableGenerationalGC();
void DisableHeapGrowingForTesting();
private:
......@@ -55,6 +57,7 @@ class V8_EXPORT_PRIVATE Heap final : public HeapBase,
Config config_;
GCInvoker gc_invoker_;
HeapGrowing growing_;
bool generational_gc_enabled_ = false;
size_t epoch_ = 0;
};
......
......@@ -25,7 +25,7 @@ AtomicEntryFlag WriteBarrier::write_barrier_enabled_;
#if defined(CPPGC_YOUNG_GENERATION)
// static
bool YoungGenerationEnabler::is_enabled_;
size_t YoungGenerationEnabler::is_enabled_;
// static
v8::base::LeakyObject<v8::base::Mutex> YoungGenerationEnabler::mutex_;
#endif // defined(CPPGC_YOUNG_GENERATION)
......@@ -209,18 +209,19 @@ bool WriteBarrierTypeForCagedHeapPolicy::IsMarking(
#if defined(CPPGC_YOUNG_GENERATION)
void YoungGenerationEnabler::Enable() {
v8::base::LockGuard _(mutex_.get());
if (is_enabled_) return;
// Enter the flag so that the check in the write barrier will always trigger
// when young generation is enabled.
WriteBarrier::FlagUpdater::Enter();
is_enabled_ = true;
if (++is_enabled_ == 1) {
// Enter the flag so that the check in the write barrier will always trigger
// when young generation is enabled.
WriteBarrier::FlagUpdater::Enter();
}
}
void YoungGenerationEnabler::DisableForTesting() {
void YoungGenerationEnabler::Disable() {
v8::base::LockGuard _(mutex_.get());
if (!is_enabled_) return;
WriteBarrier::FlagUpdater::Exit();
is_enabled_ = false;
DCHECK_LT(0, is_enabled_);
if (--is_enabled_ == 0) {
WriteBarrier::FlagUpdater::Exit();
}
}
bool YoungGenerationEnabler::IsEnabled() {
......
......@@ -25,14 +25,14 @@ class WriteBarrier::FlagUpdater final {
class V8_EXPORT_PRIVATE YoungGenerationEnabler final {
public:
static void Enable();
static void DisableForTesting();
static void Disable();
static bool IsEnabled();
private:
YoungGenerationEnabler() = delete;
static bool is_enabled_;
static size_t is_enabled_;
static v8::base::LeakyObject<v8::base::Mutex> mutex_;
};
#endif // defined(CPPGC_YOUNG_GENERATION)
......
......@@ -67,16 +67,13 @@ class MinorGCTest : public testing::TestWithHeap {
MinorGCTest() : testing::TestWithHeap() {
// Enable young generation flag and run GC. After the first run the heap
// will enable minor GC.
YoungGenerationEnabler::Enable();
Heap::From(GetHeap())->EnableGenerationalGC();
CollectMajor();
SimpleGCedBase::destructed_objects = 0;
}
~MinorGCTest() override {
YoungGenerationEnabler::DisableForTesting();
Heap::From(GetHeap())->DisableGenerationalGCForTesting();
}
~MinorGCTest() override { Heap::From(GetHeap())->Terminate(); }
static size_t DestructedObjects() {
return SimpleGCedBase::destructed_objects;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment