| // 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. |
| |
| #include "include/cppgc/allocation.h" |
| #include "include/cppgc/custom-space.h" |
| #include "src/heap/cppgc/heap-page.h" |
| #include "src/heap/cppgc/raw-heap.h" |
| #include "test/unittests/heap/cppgc/tests.h" |
| |
| namespace cppgc { |
| |
| class CustomSpace1 : public CustomSpace<CustomSpace1> { |
| public: |
| static constexpr size_t kSpaceIndex = 0; |
| }; |
| |
| class CustomSpace2 : public CustomSpace<CustomSpace2> { |
| public: |
| static constexpr size_t kSpaceIndex = 1; |
| }; |
| |
| namespace internal { |
| |
| namespace { |
| |
| size_t g_destructor_callcount; |
| |
| class TestWithHeapWithCustomSpaces : public testing::TestWithPlatform { |
| protected: |
| TestWithHeapWithCustomSpaces() { |
| Heap::HeapOptions options; |
| options.custom_spaces.emplace_back(std::make_unique<CustomSpace1>()); |
| options.custom_spaces.emplace_back(std::make_unique<CustomSpace2>()); |
| heap_ = Heap::Create(platform_, std::move(options)); |
| g_destructor_callcount = 0; |
| } |
| |
| void PreciseGC() { |
| heap_->ForceGarbageCollectionSlow( |
| ::testing::UnitTest::GetInstance()->current_test_info()->name(), |
| "Testing", cppgc::Heap::StackState::kNoHeapPointers); |
| } |
| |
| cppgc::Heap* GetHeap() const { return heap_.get(); } |
| |
| private: |
| std::unique_ptr<cppgc::Heap> heap_; |
| }; |
| |
| class RegularGCed final : public GarbageCollected<RegularGCed> { |
| public: |
| void Trace(Visitor*) const {} |
| }; |
| |
| class CustomGCed1 final : public GarbageCollected<CustomGCed1> { |
| public: |
| ~CustomGCed1() { g_destructor_callcount++; } |
| void Trace(Visitor*) const {} |
| }; |
| class CustomGCed2 final : public GarbageCollected<CustomGCed2> { |
| public: |
| ~CustomGCed2() { g_destructor_callcount++; } |
| void Trace(Visitor*) const {} |
| }; |
| |
| class CustomGCedBase : public GarbageCollected<CustomGCedBase> { |
| public: |
| void Trace(Visitor*) const {} |
| }; |
| class CustomGCedFinal1 final : public CustomGCedBase { |
| public: |
| ~CustomGCedFinal1() { g_destructor_callcount++; } |
| }; |
| class CustomGCedFinal2 final : public CustomGCedBase { |
| public: |
| ~CustomGCedFinal2() { g_destructor_callcount++; } |
| }; |
| |
| } // namespace |
| |
| } // namespace internal |
| |
| template <> |
| struct SpaceTrait<internal::CustomGCed1> { |
| using Space = CustomSpace1; |
| }; |
| |
| template <> |
| struct SpaceTrait<internal::CustomGCed2> { |
| using Space = CustomSpace2; |
| }; |
| |
| template <typename T> |
| struct SpaceTrait< |
| T, std::enable_if_t<std::is_base_of<internal::CustomGCedBase, T>::value>> { |
| using Space = CustomSpace1; |
| }; |
| |
| namespace internal { |
| |
| TEST_F(TestWithHeapWithCustomSpaces, AllocateOnCustomSpaces) { |
| auto* regular = |
| MakeGarbageCollected<RegularGCed>(GetHeap()->GetAllocationHandle()); |
| auto* custom1 = |
| MakeGarbageCollected<CustomGCed1>(GetHeap()->GetAllocationHandle()); |
| auto* custom2 = |
| MakeGarbageCollected<CustomGCed2>(GetHeap()->GetAllocationHandle()); |
| EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, |
| NormalPage::FromPayload(custom1)->space()->index()); |
| EXPECT_EQ(RawHeap::kNumberOfRegularSpaces + 1, |
| NormalPage::FromPayload(custom2)->space()->index()); |
| EXPECT_EQ(static_cast<size_t>(RawHeap::RegularSpaceType::kNormal1), |
| NormalPage::FromPayload(regular)->space()->index()); |
| } |
| |
| TEST_F(TestWithHeapWithCustomSpaces, |
| AllocateOnCustomSpacesSpecifiedThroughBase) { |
| auto* regular = |
| MakeGarbageCollected<RegularGCed>(GetHeap()->GetAllocationHandle()); |
| auto* custom1 = |
| MakeGarbageCollected<CustomGCedFinal1>(GetHeap()->GetAllocationHandle()); |
| auto* custom2 = |
| MakeGarbageCollected<CustomGCedFinal2>(GetHeap()->GetAllocationHandle()); |
| EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, |
| NormalPage::FromPayload(custom1)->space()->index()); |
| EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, |
| NormalPage::FromPayload(custom2)->space()->index()); |
| EXPECT_EQ(static_cast<size_t>(RawHeap::RegularSpaceType::kNormal1), |
| NormalPage::FromPayload(regular)->space()->index()); |
| } |
| |
| TEST_F(TestWithHeapWithCustomSpaces, SweepCustomSpace) { |
| MakeGarbageCollected<CustomGCedFinal1>(GetHeap()->GetAllocationHandle()); |
| MakeGarbageCollected<CustomGCedFinal2>(GetHeap()->GetAllocationHandle()); |
| MakeGarbageCollected<CustomGCed1>(GetHeap()->GetAllocationHandle()); |
| MakeGarbageCollected<CustomGCed2>(GetHeap()->GetAllocationHandle()); |
| EXPECT_EQ(0u, g_destructor_callcount); |
| PreciseGC(); |
| EXPECT_EQ(4u, g_destructor_callcount); |
| } |
| |
| } // namespace internal |
| |
| // Test custom space compactability. |
| |
| class CompactableCustomSpace : public CustomSpace<CompactableCustomSpace> { |
| public: |
| static constexpr size_t kSpaceIndex = 0; |
| static constexpr bool kSupportsCompaction = true; |
| }; |
| |
| class NotCompactableCustomSpace |
| : public CustomSpace<NotCompactableCustomSpace> { |
| public: |
| static constexpr size_t kSpaceIndex = 1; |
| static constexpr bool kSupportsCompaction = false; |
| }; |
| |
| class DefaultCompactableCustomSpace |
| : public CustomSpace<DefaultCompactableCustomSpace> { |
| public: |
| static constexpr size_t kSpaceIndex = 2; |
| // By default space are not compactable. |
| }; |
| |
| namespace internal { |
| namespace { |
| |
| class TestWithHeapWithCompactableCustomSpaces |
| : public testing::TestWithPlatform { |
| protected: |
| TestWithHeapWithCompactableCustomSpaces() { |
| Heap::HeapOptions options; |
| options.custom_spaces.emplace_back( |
| std::make_unique<CompactableCustomSpace>()); |
| options.custom_spaces.emplace_back( |
| std::make_unique<NotCompactableCustomSpace>()); |
| options.custom_spaces.emplace_back( |
| std::make_unique<DefaultCompactableCustomSpace>()); |
| heap_ = Heap::Create(platform_, std::move(options)); |
| g_destructor_callcount = 0; |
| } |
| |
| void PreciseGC() { |
| heap_->ForceGarbageCollectionSlow("TestWithHeapWithCompactableCustomSpaces", |
| "Testing", |
| cppgc::Heap::StackState::kNoHeapPointers); |
| } |
| |
| cppgc::Heap* GetHeap() const { return heap_.get(); } |
| |
| private: |
| std::unique_ptr<cppgc::Heap> heap_; |
| }; |
| |
| class CompactableGCed final : public GarbageCollected<CompactableGCed> { |
| public: |
| void Trace(Visitor*) const {} |
| }; |
| class NotCompactableGCed final : public GarbageCollected<NotCompactableGCed> { |
| public: |
| void Trace(Visitor*) const {} |
| }; |
| class DefaultCompactableGCed final |
| : public GarbageCollected<DefaultCompactableGCed> { |
| public: |
| void Trace(Visitor*) const {} |
| }; |
| |
| } // namespace |
| } // namespace internal |
| |
| template <> |
| struct SpaceTrait<internal::CompactableGCed> { |
| using Space = CompactableCustomSpace; |
| }; |
| template <> |
| struct SpaceTrait<internal::NotCompactableGCed> { |
| using Space = NotCompactableCustomSpace; |
| }; |
| template <> |
| struct SpaceTrait<internal::DefaultCompactableGCed> { |
| using Space = DefaultCompactableCustomSpace; |
| }; |
| |
| namespace internal { |
| |
| TEST_F(TestWithHeapWithCompactableCustomSpaces, |
| AllocateOnCompactableCustomSpaces) { |
| auto* compactable = |
| MakeGarbageCollected<CompactableGCed>(GetHeap()->GetAllocationHandle()); |
| auto* not_compactable = MakeGarbageCollected<NotCompactableGCed>( |
| GetHeap()->GetAllocationHandle()); |
| auto* default_compactable = MakeGarbageCollected<DefaultCompactableGCed>( |
| GetHeap()->GetAllocationHandle()); |
| EXPECT_TRUE(NormalPage::FromPayload(compactable)->space()->is_compactable()); |
| EXPECT_FALSE( |
| NormalPage::FromPayload(not_compactable)->space()->is_compactable()); |
| EXPECT_FALSE( |
| NormalPage::FromPayload(default_compactable)->space()->is_compactable()); |
| } |
| |
| } // namespace internal |
| |
| } // namespace cppgc |