// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if defined(CPPGC_YOUNG_GENERATION) #include "include/cppgc/allocation.h" #include "include/cppgc/persistent.h" #include "src/heap/cppgc/heap-object-header-inl.h" #include "src/heap/cppgc/heap.h" #include "test/unittests/heap/cppgc/tests.h" #include "testing/gtest/include/gtest/gtest.h" namespace cppgc { namespace internal { namespace { class SimpleGCedBase : public GarbageCollected { public: static size_t destructed_objects; virtual ~SimpleGCedBase() { ++destructed_objects; } virtual void Trace(Visitor* v) const { v->Trace(next); } Member next; }; size_t SimpleGCedBase::destructed_objects; template class SimpleGCed final : public SimpleGCedBase { char array[Size]; }; using Small = SimpleGCed<64>; using Large = SimpleGCed; template struct OtherType; template <> struct OtherType { using Type = Large; }; template <> struct OtherType { using Type = Small; }; class MinorGCTest : public testing::TestWithHeap { public: MinorGCTest() { CollectMajor(); SimpleGCedBase::destructed_objects = 0; } static size_t DestructedObjects() { return SimpleGCedBase::destructed_objects; } void CollectMinor() { Heap::From(GetHeap())->CollectGarbage( Heap::Config::MinorPreciseAtomicConfig()); } void CollectMajor() { Heap::From(GetHeap())->CollectGarbage(Heap::Config::PreciseAtomicConfig()); } }; template class MinorGCTestForType : public MinorGCTest { public: using Type = SmallOrLarge; }; } // namespace using ObjectTypes = ::testing::Types; TYPED_TEST_SUITE(MinorGCTestForType, ObjectTypes); TYPED_TEST(MinorGCTestForType, MinorCollection) { using Type = typename TestFixture::Type; MakeGarbageCollected(this->GetAllocationHandle()); EXPECT_EQ(0u, TestFixture::DestructedObjects()); MinorGCTest::CollectMinor(); EXPECT_EQ(1u, TestFixture::DestructedObjects()); { Heap::NoGCScope no_gc_scope_(*Heap::From(this->GetHeap())); Type* prev = nullptr; for (size_t i = 0; i < 64; ++i) { auto* ptr = MakeGarbageCollected(this->GetAllocationHandle()); ptr->next = prev; prev = ptr; } } MinorGCTest::CollectMinor(); EXPECT_EQ(65u, TestFixture::DestructedObjects()); } TYPED_TEST(MinorGCTestForType, StickyBits) { using Type = typename TestFixture::Type; Persistent p1 = MakeGarbageCollected(this->GetAllocationHandle()); TestFixture::CollectMinor(); EXPECT_FALSE(HeapObjectHeader::FromPayload(p1.Get()).IsYoung()); TestFixture::CollectMajor(); EXPECT_FALSE(HeapObjectHeader::FromPayload(p1.Get()).IsYoung()); EXPECT_EQ(0u, TestFixture::DestructedObjects()); } TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) { using Type = typename TestFixture::Type; Persistent p = MakeGarbageCollected(this->GetAllocationHandle()); TestFixture::CollectMinor(); EXPECT_EQ(0u, TestFixture::DestructedObjects()); EXPECT_FALSE(HeapObjectHeader::FromPayload(p.Get()).IsYoung()); // Check that the old deleted object won't be visited during minor GC. Type* raw = p.Release(); TestFixture::CollectMinor(); EXPECT_EQ(0u, TestFixture::DestructedObjects()); EXPECT_FALSE(HeapObjectHeader::FromPayload(raw).IsYoung()); EXPECT_FALSE(HeapObjectHeader::FromPayload(raw).IsFree()); // Check that the old deleted object will be revisited in major GC. TestFixture::CollectMajor(); EXPECT_EQ(1u, TestFixture::DestructedObjects()); } template void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) { Persistent old = MakeGarbageCollected(heap->GetAllocationHandle()); test->CollectMinor(); EXPECT_FALSE(HeapObjectHeader::FromPayload(old.Get()).IsYoung()); Type2* young = nullptr; { Heap::NoGCScope no_gc_scope_(*Heap::From(heap)); // Allocate young objects. for (size_t i = 0; i < 64; ++i) { auto* ptr = MakeGarbageCollected(heap->GetAllocationHandle()); ptr->next = young; young = ptr; EXPECT_TRUE(HeapObjectHeader::FromPayload(young).IsYoung()); } } const auto& set = Heap::From(heap)->remembered_slots(); auto set_size_before = set.size(); // Issue generational barrier. old->next = young; EXPECT_EQ(set_size_before + 1u, set.size()); // Check that the remembered set is visited. test->CollectMinor(); EXPECT_EQ(0u, MinorGCTest::DestructedObjects()); EXPECT_TRUE(set.empty()); for (size_t i = 0; i < 64; ++i) { EXPECT_FALSE(HeapObjectHeader::FromPayload(young).IsFree()); EXPECT_FALSE(HeapObjectHeader::FromPayload(young).IsYoung()); young = static_cast(young->next.Get()); } old.Release(); test->CollectMajor(); EXPECT_EQ(65u, MinorGCTest::DestructedObjects()); } TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForSamePageTypes) { using Type = typename TestFixture::Type; InterGenerationalPointerTest(this, this->GetHeap()); } TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForDifferentPageTypes) { using Type = typename TestFixture::Type; InterGenerationalPointerTest::Type>( this, this->GetHeap()); } TYPED_TEST(MinorGCTestForType, OmitGenerationalBarrierForOnStackObject) { using Type = typename TestFixture::Type; class StackAllocated : GarbageCollected { CPPGC_STACK_ALLOCATED(); public: Type* ptr = nullptr; } stack_object; auto* new_object = MakeGarbageCollected(this->GetAllocationHandle()); const auto& set = Heap::From(this->GetHeap())->remembered_slots(); const size_t set_size_before_barrier = set.size(); // Try issuing generational barrier for on-stack object. stack_object.ptr = new_object; WriteBarrier::MarkingBarrier(reinterpret_cast(&stack_object.ptr), new_object); EXPECT_EQ(set_size_before_barrier, set.size()); } TYPED_TEST(MinorGCTestForType, OmitGenerationalBarrierForSentinels) { using Type = typename TestFixture::Type; Persistent old = MakeGarbageCollected(this->GetAllocationHandle()); TestFixture::CollectMinor(); EXPECT_FALSE(HeapObjectHeader::FromPayload(old.Get()).IsYoung()); const auto& set = Heap::From(this->GetHeap())->remembered_slots(); const size_t set_size_before_barrier = set.size(); // Try issuing generational barrier for nullptr. old->next = static_cast(nullptr); EXPECT_EQ(set_size_before_barrier, set.size()); // Try issuing generational barrier for sentinel. old->next = static_cast(kSentinelPointer); EXPECT_EQ(set_size_before_barrier, set.size()); } } // namespace internal } // namespace cppgc #endif