v8/test/unittests/heap/cppgc/minor-gc-unittest.cc
Omer Katz 86af7c6a76 cppgc: Rename HoH methods
Replaces Payload* terminiology with Object* terminology.
HoH::ObjectSize = just the object, without the header.
HoH::AllocatedSize = both the object and the header.

Payload terminology is retained only for pages.

Bug: chromium:1056170
Change-Id: I568a324ae8728f098be642b024493c375ec873cb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2892079
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74538}
2021-05-12 18:54:43 +00:00

237 lines
6.8 KiB
C++

// 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/heap-consistency.h"
#include "include/cppgc/persistent.h"
#include "src/heap/cppgc/heap-object-header.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<SimpleGCedBase> {
public:
static size_t destructed_objects;
virtual ~SimpleGCedBase() { ++destructed_objects; }
virtual void Trace(Visitor* v) const { v->Trace(next); }
Member<SimpleGCedBase> next;
};
size_t SimpleGCedBase::destructed_objects;
template <size_t Size>
class SimpleGCed final : public SimpleGCedBase {
char array[Size];
};
using Small = SimpleGCed<64>;
using Large = SimpleGCed<kLargeObjectSizeThreshold * 2>;
template <typename Type>
struct OtherType;
template <>
struct OtherType<Small> {
using Type = Large;
};
template <>
struct OtherType<Large> {
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 <typename SmallOrLarge>
class MinorGCTestForType : public MinorGCTest {
public:
using Type = SmallOrLarge;
};
} // namespace
using ObjectTypes = ::testing::Types<Small, Large>;
TYPED_TEST_SUITE(MinorGCTestForType, ObjectTypes);
TYPED_TEST(MinorGCTestForType, MinorCollection) {
using Type = typename TestFixture::Type;
MakeGarbageCollected<Type>(this->GetAllocationHandle());
EXPECT_EQ(0u, TestFixture::DestructedObjects());
MinorGCTest::CollectMinor();
EXPECT_EQ(1u, TestFixture::DestructedObjects());
{
subtle::NoGarbageCollectionScope no_gc_scope(*Heap::From(this->GetHeap()));
Type* prev = nullptr;
for (size_t i = 0; i < 64; ++i) {
auto* ptr = MakeGarbageCollected<Type>(this->GetAllocationHandle());
ptr->next = prev;
prev = ptr;
}
}
MinorGCTest::CollectMinor();
EXPECT_EQ(65u, TestFixture::DestructedObjects());
}
TYPED_TEST(MinorGCTestForType, StickyBits) {
using Type = typename TestFixture::Type;
Persistent<Type> p1 = MakeGarbageCollected<Type>(this->GetAllocationHandle());
TestFixture::CollectMinor();
EXPECT_FALSE(HeapObjectHeader::FromObject(p1.Get()).IsYoung());
TestFixture::CollectMajor();
EXPECT_FALSE(HeapObjectHeader::FromObject(p1.Get()).IsYoung());
EXPECT_EQ(0u, TestFixture::DestructedObjects());
}
TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) {
using Type = typename TestFixture::Type;
Persistent<Type> p = MakeGarbageCollected<Type>(this->GetAllocationHandle());
TestFixture::CollectMinor();
EXPECT_EQ(0u, TestFixture::DestructedObjects());
EXPECT_FALSE(HeapObjectHeader::FromObject(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::FromObject(raw).IsYoung());
EXPECT_FALSE(HeapObjectHeader::FromObject(raw).IsFree());
// Check that the old deleted object will be revisited in major GC.
TestFixture::CollectMajor();
EXPECT_EQ(1u, TestFixture::DestructedObjects());
}
template <typename Type1, typename Type2>
void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) {
Persistent<Type1> old =
MakeGarbageCollected<Type1>(heap->GetAllocationHandle());
test->CollectMinor();
EXPECT_FALSE(HeapObjectHeader::FromObject(old.Get()).IsYoung());
Type2* young = nullptr;
{
subtle::NoGarbageCollectionScope no_gc_scope(*Heap::From(heap));
// Allocate young objects.
for (size_t i = 0; i < 64; ++i) {
auto* ptr = MakeGarbageCollected<Type2>(heap->GetAllocationHandle());
ptr->next = young;
young = ptr;
EXPECT_TRUE(HeapObjectHeader::FromObject(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::FromObject(young).IsFree());
EXPECT_FALSE(HeapObjectHeader::FromObject(young).IsYoung());
young = static_cast<Type2*>(young->next.Get());
}
old.Release();
test->CollectMajor();
EXPECT_EQ(65u, MinorGCTest::DestructedObjects());
}
TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForSamePageTypes) {
using Type = typename TestFixture::Type;
InterGenerationalPointerTest<Type, Type>(this, this->GetHeap());
}
TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForDifferentPageTypes) {
using Type = typename TestFixture::Type;
InterGenerationalPointerTest<Type, typename OtherType<Type>::Type>(
this, this->GetHeap());
}
TYPED_TEST(MinorGCTestForType, OmitGenerationalBarrierForOnStackObject) {
using Type = typename TestFixture::Type;
class StackAllocated : GarbageCollected<StackAllocated> {
CPPGC_STACK_ALLOCATED();
public:
Type* ptr = nullptr;
} stack_object;
// Try issuing generational barrier for on-stack object.
stack_object.ptr = MakeGarbageCollected<Type>(this->GetAllocationHandle());
subtle::HeapConsistency::WriteBarrierParams params;
EXPECT_EQ(subtle::HeapConsistency::WriteBarrierType::kNone,
subtle::HeapConsistency::GetWriteBarrierType(
reinterpret_cast<void*>(&stack_object.ptr), stack_object.ptr,
params));
}
TYPED_TEST(MinorGCTestForType, OmitGenerationalBarrierForSentinels) {
using Type = typename TestFixture::Type;
Persistent<Type> old =
MakeGarbageCollected<Type>(this->GetAllocationHandle());
TestFixture::CollectMinor();
EXPECT_FALSE(HeapObjectHeader::FromObject(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<Type*>(nullptr);
EXPECT_EQ(set_size_before_barrier, set.size());
// Try issuing generational barrier for sentinel.
old->next = static_cast<Type*>(kSentinelPointer);
EXPECT_EQ(set_size_before_barrier, set.size());
}
} // namespace internal
} // namespace cppgc
#endif