cppgc: Add support for prefinalizers
This CL adds: - Declaring and invoking prefinalizers - NoAllocationScope Bug: chromium:1056170 Change-Id: Ib0f688fa4a8bb5fde44b36597ce2d6d2664fdff5 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2139588 Commit-Queue: Omer Katz <omerkatz@chromium.org> Reviewed-by: Anton Bikineev <bikineev@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#67125}
This commit is contained in:
parent
efea740768
commit
28bc264947
7
BUILD.gn
7
BUILD.gn
@ -3950,12 +3950,17 @@ v8_source_set("cppgc_base") {
|
||||
"include/cppgc/heap.h",
|
||||
"include/cppgc/internal/accessors.h",
|
||||
"include/cppgc/internal/api-contants.h",
|
||||
"include/cppgc/internal/compiler-specific.h",
|
||||
"include/cppgc/internal/finalizer-traits.h",
|
||||
"include/cppgc/internal/gc-info.h",
|
||||
"include/cppgc/internal/pointer-policies.h",
|
||||
"include/cppgc/internal/prefinalizer-handler.h",
|
||||
"include/cppgc/liveness-broker.h",
|
||||
"include/cppgc/liveness-broker.h",
|
||||
"include/cppgc/macros.h",
|
||||
"include/cppgc/member.h",
|
||||
"include/cppgc/platform.h",
|
||||
"include/cppgc/prefinalizer.h",
|
||||
"include/cppgc/source-location.h",
|
||||
"include/cppgc/trace-trait.h",
|
||||
"include/cppgc/type-traits.h",
|
||||
@ -3979,6 +3984,8 @@ v8_source_set("cppgc_base") {
|
||||
"src/heap/cppgc/page-memory.h",
|
||||
"src/heap/cppgc/platform.cc",
|
||||
"src/heap/cppgc/pointer-policies.cc",
|
||||
"src/heap/cppgc/prefinalizer-handler.cc",
|
||||
"src/heap/cppgc/prefinalizer-handler.h",
|
||||
"src/heap/cppgc/sanitizers.h",
|
||||
"src/heap/cppgc/source-location.cc",
|
||||
"src/heap/cppgc/stack.cc",
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "include/cppgc/internal/api-constants.h"
|
||||
#include "include/cppgc/macros.h"
|
||||
#include "include/cppgc/platform.h"
|
||||
#include "include/cppgc/trace-trait.h"
|
||||
#include "include/cppgc/type-traits.h"
|
||||
@ -121,12 +122,6 @@ class GarbageCollectedMixin : public internal::GarbageCollectedBase {
|
||||
virtual void Trace(cppgc::Visitor*) {}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
class __thisIsHereToForceASemicolonAfterThisMacro {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Macro defines all methods and markers needed for handling mixins. Must be
|
||||
* used on the type that is inheriting from GarbageCollected *and*
|
||||
|
26
include/cppgc/internal/compiler-specific.h
Normal file
26
include/cppgc/internal/compiler-specific.h
Normal file
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#ifndef INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_
|
||||
#define INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
#if defined(__has_cpp_attribute)
|
||||
#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) __has_cpp_attribute(FEATURE)
|
||||
#else
|
||||
#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) 0
|
||||
#endif
|
||||
|
||||
// [[no_unique_address]] comes in C++20 but supported in clang with -std >=
|
||||
// c++11.
|
||||
#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) // NOLINTNEXTLINE
|
||||
#define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||
#else
|
||||
#define CPPGC_NO_UNIQUE_ADDRESS
|
||||
#endif
|
||||
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_
|
31
include/cppgc/internal/prefinalizer-handler.h
Normal file
31
include/cppgc/internal/prefinalizer-handler.h
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
#ifndef INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_
|
||||
#define INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_
|
||||
|
||||
#include "include/cppgc/heap.h"
|
||||
#include "include/cppgc/liveness-broker.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
class V8_EXPORT PreFinalizerRegistrationDispatcher final {
|
||||
public:
|
||||
using PreFinalizerCallback = bool (*)(const LivenessBroker&, void*);
|
||||
struct PreFinalizer {
|
||||
void* object_;
|
||||
PreFinalizerCallback callback_;
|
||||
|
||||
bool operator==(const PreFinalizer& other);
|
||||
};
|
||||
|
||||
static void RegisterPrefinalizer(cppgc::Heap* heap,
|
||||
PreFinalizer prefinalzier);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_
|
26
include/cppgc/macros.h
Normal file
26
include/cppgc/macros.h
Normal file
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#ifndef INCLUDE_CPPGC_MACROS_H_
|
||||
#define INCLUDE_CPPGC_MACROS_H_
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
namespace internal {
|
||||
class __thisIsHereToForceASemicolonAfterThisMacro {};
|
||||
} // namespace internal
|
||||
|
||||
// Use if the object is only stack allocated.
|
||||
#define CPPGC_STACK_ALLOCATED() \
|
||||
public: \
|
||||
using IsStackAllocatedTypeMarker = int; \
|
||||
\
|
||||
private: \
|
||||
void* operator new(size_t) = delete; \
|
||||
void* operator new(size_t, void*) = delete; \
|
||||
friend class internal::__thisIsHereToForceASemicolonAfterThisMacro
|
||||
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_MACROS_H_
|
54
include/cppgc/prefinalizer.h
Normal file
54
include/cppgc/prefinalizer.h
Normal file
@ -0,0 +1,54 @@
|
||||
// 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.
|
||||
|
||||
#ifndef INCLUDE_CPPGC_PREFINALIZER_H_
|
||||
#define INCLUDE_CPPGC_PREFINALIZER_H_
|
||||
|
||||
#include "include/cppgc/internal/accessors.h"
|
||||
#include "include/cppgc/internal/compiler-specific.h"
|
||||
#include "include/cppgc/internal/prefinalizer-handler.h"
|
||||
#include "include/cppgc/liveness-broker.h"
|
||||
#include "include/cppgc/macros.h"
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
class PrefinalizerRegistration final {
|
||||
public:
|
||||
explicit PrefinalizerRegistration(T* self) {
|
||||
static_assert(sizeof(&T::InvokePreFinalizer) > 0,
|
||||
"USING_PRE_FINALIZER(T) must be defined.");
|
||||
|
||||
cppgc::internal::PreFinalizerRegistrationDispatcher::RegisterPrefinalizer(
|
||||
internal::GetHeapFromPayload(self), {self, T::InvokePreFinalizer});
|
||||
}
|
||||
|
||||
void* operator new(size_t, void* location) = delete;
|
||||
void* operator new(size_t) = delete;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#define CPPGC_USING_PRE_FINALIZER(Class, PreFinalizer) \
|
||||
public: \
|
||||
static bool InvokePreFinalizer(const LivenessBroker& liveness_broker, \
|
||||
void* object) { \
|
||||
static_assert(internal::IsGarbageCollectedTypeV<Class>, \
|
||||
"Only garbage collected objects can have prefinalizers"); \
|
||||
Class* self = static_cast<Class*>(object); \
|
||||
if (liveness_broker.IsHeapObjectAlive(self)) return false; \
|
||||
self->Class::PreFinalizer(); \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
CPPGC_NO_UNIQUE_ADDRESS internal::PrefinalizerRegistration<Class> \
|
||||
prefinalizer_dummy_{this}; \
|
||||
friend class internal::__thisIsHereToForceASemicolonAfterThisMacro
|
||||
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // INCLUDE_CPPGC_PREFINALIZER_H_
|
@ -14,6 +14,7 @@ namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
void* Heap::Allocate(size_t size, GCInfoIndex index) {
|
||||
DCHECK(is_allocation_allowed());
|
||||
// TODO(chromium:1056170): This is merely a dummy implementation and will be
|
||||
// replaced with proper allocation code throughout the migration.
|
||||
size_t allocation_size = size + sizeof(HeapObjectHeader);
|
||||
|
@ -54,7 +54,8 @@ class StackMarker final : public StackVisitor {
|
||||
|
||||
Heap::Heap()
|
||||
: stack_(std::make_unique<Stack>(v8::base::Stack::GetStackStart())),
|
||||
allocator_(std::make_unique<BasicAllocator>(this)) {}
|
||||
allocator_(std::make_unique<BasicAllocator>(this)),
|
||||
prefinalizer_handler_(std::make_unique<PreFinalizerHandler>()) {}
|
||||
|
||||
Heap::~Heap() {
|
||||
for (HeapObjectHeader* header : objects_) {
|
||||
@ -73,6 +74,11 @@ void Heap::CollectGarbage(GCConfig config) {
|
||||
stack_->IteratePointers(&marker);
|
||||
}
|
||||
// "Sweeping and finalization".
|
||||
{
|
||||
// Pre finalizers are forbidden from allocating objects
|
||||
NoAllocationScope no_allocation_scope_(this);
|
||||
prefinalizer_handler_->InvokePreFinalizers();
|
||||
}
|
||||
for (auto it = objects_.begin(); it != objects_.end();) {
|
||||
HeapObjectHeader* header = *it;
|
||||
if (header->IsMarked()) {
|
||||
@ -89,6 +95,11 @@ Heap::NoGCScope::NoGCScope(Heap* heap) : heap_(heap) { heap_->no_gc_scope_++; }
|
||||
|
||||
Heap::NoGCScope::~NoGCScope() { heap_->no_gc_scope_--; }
|
||||
|
||||
Heap::NoAllocationScope::NoAllocationScope(Heap* heap) : heap_(heap) {
|
||||
heap_->no_allocation_scope_++;
|
||||
}
|
||||
Heap::NoAllocationScope::~NoAllocationScope() { heap_->no_allocation_scope_--; }
|
||||
|
||||
Heap::BasicAllocator::BasicAllocator(Heap* heap) : heap_(heap) {}
|
||||
|
||||
Heap::BasicAllocator::~BasicAllocator() {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "include/cppgc/internal/gc-info.h"
|
||||
#include "include/cppgc/liveness-broker.h"
|
||||
#include "src/heap/cppgc/heap-object-header.h"
|
||||
#include "src/heap/cppgc/prefinalizer-handler.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
@ -35,6 +36,21 @@ class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
|
||||
Heap* heap_;
|
||||
};
|
||||
|
||||
// The NoAllocationScope class is used in debug mode to catch unwanted
|
||||
// allocations. E.g. allocations during GC.
|
||||
class V8_EXPORT_PRIVATE NoAllocationScope final {
|
||||
CPPGC_STACK_ALLOCATED();
|
||||
|
||||
public:
|
||||
explicit NoAllocationScope(Heap* heap);
|
||||
~NoAllocationScope();
|
||||
|
||||
private:
|
||||
Heap* const heap_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NoAllocationScope);
|
||||
};
|
||||
|
||||
struct GCConfig {
|
||||
enum class StackState : uint8_t {
|
||||
kEmpty,
|
||||
@ -55,6 +71,10 @@ class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
|
||||
|
||||
void CollectGarbage(GCConfig config = GCConfig::Default());
|
||||
|
||||
PreFinalizerHandler* prefinalizer_handler() {
|
||||
return prefinalizer_handler_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO(chromium:1056170): Remove as soon as arenas are available for
|
||||
// allocation.
|
||||
@ -79,11 +99,15 @@ class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
|
||||
|
||||
bool in_no_gc_scope() { return no_gc_scope_ > 0; }
|
||||
|
||||
bool is_allocation_allowed() { return no_allocation_scope_ == 0; }
|
||||
|
||||
std::unique_ptr<Stack> stack_;
|
||||
std::unique_ptr<BasicAllocator> allocator_;
|
||||
std::vector<HeapObjectHeader*> objects_;
|
||||
std::unique_ptr<PreFinalizerHandler> prefinalizer_handler_;
|
||||
|
||||
size_t no_gc_scope_ = 0;
|
||||
size_t no_allocation_scope_ = 0;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
65
src/heap/cppgc/prefinalizer-handler.cc
Normal file
65
src/heap/cppgc/prefinalizer-handler.cc
Normal file
@ -0,0 +1,65 @@
|
||||
// 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 "src/heap/cppgc/prefinalizer-handler.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/heap/cppgc/heap.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
// static
|
||||
void PreFinalizerRegistrationDispatcher::RegisterPrefinalizer(
|
||||
cppgc::Heap* heap, PreFinalizer prefinalzier) {
|
||||
internal::Heap::From(heap)->prefinalizer_handler()->RegisterPrefinalizer(
|
||||
prefinalzier);
|
||||
}
|
||||
|
||||
bool PreFinalizerRegistrationDispatcher::PreFinalizer::operator==(
|
||||
const PreFinalizer& other) {
|
||||
return (object_ == other.object_) && (callback_ == other.callback_);
|
||||
}
|
||||
|
||||
PreFinalizerHandler::PreFinalizerHandler()
|
||||
#ifdef DEBUG
|
||||
: creation_thread_id_(v8::base::OS::GetCurrentThreadId())
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
void PreFinalizerHandler::RegisterPrefinalizer(PreFinalizer prefinalizer) {
|
||||
DCHECK(CurrentThreadIsCreationThread());
|
||||
DCHECK_EQ(ordered_pre_finalizers_.end(),
|
||||
std::find(ordered_pre_finalizers_.begin(),
|
||||
ordered_pre_finalizers_.end(), prefinalizer));
|
||||
ordered_pre_finalizers_.push_back(prefinalizer);
|
||||
}
|
||||
|
||||
void PreFinalizerHandler::InvokePreFinalizers() {
|
||||
DCHECK(CurrentThreadIsCreationThread());
|
||||
LivenessBroker liveness_broker = LivenessBrokerFactory::Create();
|
||||
ordered_pre_finalizers_.erase(
|
||||
ordered_pre_finalizers_.begin(),
|
||||
std::remove_if(ordered_pre_finalizers_.rbegin(),
|
||||
ordered_pre_finalizers_.rend(),
|
||||
[liveness_broker](const PreFinalizer& pf) {
|
||||
return (pf.callback_)(liveness_broker, pf.object_);
|
||||
})
|
||||
.base());
|
||||
ordered_pre_finalizers_.shrink_to_fit();
|
||||
}
|
||||
|
||||
bool PreFinalizerHandler::CurrentThreadIsCreationThread() {
|
||||
#ifdef DEBUG
|
||||
return creation_thread_id_ == v8::base::OS::GetCurrentThreadId();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
44
src/heap/cppgc/prefinalizer-handler.h
Normal file
44
src/heap/cppgc/prefinalizer-handler.h
Normal file
@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
#ifndef V8_HEAP_CPPGC_PREFINALIZER_HANDLER_H_
|
||||
#define V8_HEAP_CPPGC_PREFINALIZER_HANDLER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "include/cppgc/prefinalizer.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
class PreFinalizerHandler final {
|
||||
public:
|
||||
using PreFinalizer =
|
||||
cppgc::internal::PreFinalizerRegistrationDispatcher::PreFinalizer;
|
||||
|
||||
PreFinalizerHandler();
|
||||
|
||||
void RegisterPrefinalizer(PreFinalizer prefinalzier);
|
||||
|
||||
void InvokePreFinalizers();
|
||||
|
||||
private:
|
||||
// Checks that the current thread is the thread that created the heap.
|
||||
bool CurrentThreadIsCreationThread();
|
||||
|
||||
// Pre-finalizers are called in the reverse order in which they are
|
||||
// registered by the constructors (including constructors of Mixin
|
||||
// objects) for an object, by processing the ordered_pre_finalizers_
|
||||
// back-to-front.
|
||||
std::vector<PreFinalizer> ordered_pre_finalizers_;
|
||||
|
||||
#ifdef DEBUG
|
||||
int creation_thread_id_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // V8_HEAP_CPPGC_PREFINALIZER_HANDLER_H_
|
@ -52,6 +52,7 @@ v8_source_set("cppgc_unittests_sources") {
|
||||
"heap/cppgc/heap_unittest.cc",
|
||||
"heap/cppgc/member_unittests.cc",
|
||||
"heap/cppgc/page-memory_unittest.cc",
|
||||
"heap/cppgc/prefinalizer_unittests.cc",
|
||||
"heap/cppgc/source-location_unittest.cc",
|
||||
"heap/cppgc/stack_unittest.cc",
|
||||
"heap/cppgc/tests.cc",
|
||||
|
199
test/unittests/heap/cppgc/prefinalizer_unittests.cc
Normal file
199
test/unittests/heap/cppgc/prefinalizer_unittests.cc
Normal file
@ -0,0 +1,199 @@
|
||||
// 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/prefinalizer.h"
|
||||
|
||||
#include "include/cppgc/allocation.h"
|
||||
#include "include/cppgc/garbage-collected.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 PrefinalizerTest : public testing::TestWithHeap {};
|
||||
|
||||
class GCed : public GarbageCollected<GCed> {
|
||||
CPPGC_USING_PRE_FINALIZER(GCed, PreFinalizer);
|
||||
|
||||
public:
|
||||
void Trace(Visitor*) {}
|
||||
void PreFinalizer() { ++prefinalizer_callcount; }
|
||||
|
||||
static size_t prefinalizer_callcount;
|
||||
};
|
||||
size_t GCed::prefinalizer_callcount = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerCalledOnDeadObject) {
|
||||
GCed::prefinalizer_callcount = 0;
|
||||
auto* object = MakeGarbageCollected<GCed>(GetHeap());
|
||||
USE(object);
|
||||
EXPECT_EQ(0u, GCed::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, GCed::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, GCed::prefinalizer_callcount);
|
||||
}
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerNotCalledOnLiveObject) {
|
||||
GCed::prefinalizer_callcount = 0;
|
||||
auto* object = MakeGarbageCollected<GCed>(GetHeap());
|
||||
HeapObjectHeader::FromPayload(object).TryMarkAtomic();
|
||||
EXPECT_EQ(0u, GCed::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(0u, GCed::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, GCed::prefinalizer_callcount);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class Mixin : public GarbageCollectedMixin {
|
||||
CPPGC_USING_PRE_FINALIZER(Mixin, PreFinalizer);
|
||||
|
||||
public:
|
||||
void PreFinalizer() { ++prefinalizer_callcount; }
|
||||
|
||||
static size_t prefinalizer_callcount;
|
||||
};
|
||||
size_t Mixin::prefinalizer_callcount = 0;
|
||||
|
||||
class GCedWithMixin : public GarbageCollected<GCedWithMixin>, public Mixin {
|
||||
USING_GARBAGE_COLLECTED_MIXIN();
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerCalledOnDeadMixinObject) {
|
||||
Mixin::prefinalizer_callcount = 0;
|
||||
auto* object = MakeGarbageCollected<GCedWithMixin>(GetHeap());
|
||||
USE(object);
|
||||
EXPECT_EQ(0u, Mixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, Mixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, Mixin::prefinalizer_callcount);
|
||||
}
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerNotCalledOnLiveMixinObject) {
|
||||
Mixin::prefinalizer_callcount = 0;
|
||||
auto* object = MakeGarbageCollected<GCedWithMixin>(GetHeap());
|
||||
HeapObjectHeader::FromPayload(object).TryMarkAtomic();
|
||||
EXPECT_EQ(0u, Mixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(0u, Mixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, Mixin::prefinalizer_callcount);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class BaseMixin : public GarbageCollectedMixin {
|
||||
CPPGC_USING_PRE_FINALIZER(BaseMixin, PreFinalizer);
|
||||
|
||||
public:
|
||||
void PreFinalizer();
|
||||
|
||||
static size_t prefinalizer_callcount;
|
||||
};
|
||||
size_t BaseMixin::prefinalizer_callcount = 0;
|
||||
|
||||
class InheritingMixin : public BaseMixin {
|
||||
CPPGC_USING_PRE_FINALIZER(InheritingMixin, PreFinalizer);
|
||||
|
||||
public:
|
||||
void PreFinalizer();
|
||||
|
||||
static size_t prefinalizer_callcount;
|
||||
};
|
||||
size_t InheritingMixin::prefinalizer_callcount = 0;
|
||||
|
||||
class GCedWithMixins : public GarbageCollected<GCedWithMixins>,
|
||||
public InheritingMixin {
|
||||
USING_GARBAGE_COLLECTED_MIXIN();
|
||||
CPPGC_USING_PRE_FINALIZER(GCedWithMixins, PreFinalizer);
|
||||
|
||||
public:
|
||||
void PreFinalizer();
|
||||
|
||||
static size_t prefinalizer_callcount;
|
||||
};
|
||||
size_t GCedWithMixins::prefinalizer_callcount = 0;
|
||||
|
||||
void BaseMixin::PreFinalizer() {
|
||||
EXPECT_EQ(1u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(1u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, BaseMixin::prefinalizer_callcount);
|
||||
++BaseMixin::prefinalizer_callcount;
|
||||
}
|
||||
|
||||
void InheritingMixin::PreFinalizer() {
|
||||
EXPECT_EQ(1u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, BaseMixin::prefinalizer_callcount);
|
||||
InheritingMixin::prefinalizer_callcount = true;
|
||||
}
|
||||
|
||||
void GCedWithMixins::PreFinalizer() {
|
||||
EXPECT_EQ(0u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, BaseMixin::prefinalizer_callcount);
|
||||
GCedWithMixins::prefinalizer_callcount = true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerInvocationPreservesOrder) {
|
||||
BaseMixin::prefinalizer_callcount = 0;
|
||||
InheritingMixin::prefinalizer_callcount = 0;
|
||||
GCedWithMixins::prefinalizer_callcount = 0;
|
||||
auto* object = MakeGarbageCollected<GCedWithMixins>(GetHeap());
|
||||
USE(object);
|
||||
EXPECT_EQ(0u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(0u, BaseMixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(1u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(1u, BaseMixin::prefinalizer_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, GCedWithMixins::prefinalizer_callcount);
|
||||
EXPECT_EQ(1u, InheritingMixin::prefinalizer_callcount);
|
||||
EXPECT_EQ(1u, BaseMixin::prefinalizer_callcount);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class AllocatingPrefinalizer : public GarbageCollected<AllocatingPrefinalizer> {
|
||||
CPPGC_USING_PRE_FINALIZER(AllocatingPrefinalizer, PreFinalizer);
|
||||
|
||||
public:
|
||||
explicit AllocatingPrefinalizer(cppgc::Heap* heap) : heap_(heap) {}
|
||||
void Trace(Visitor*) {}
|
||||
void PreFinalizer() { MakeGarbageCollected<GCed>(heap_); }
|
||||
|
||||
private:
|
||||
cppgc::Heap* heap_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
TEST_F(PrefinalizerTest, PrefinalizerFailsOnAllcoation) {
|
||||
auto* object =
|
||||
MakeGarbageCollected<AllocatingPrefinalizer>(GetHeap(), GetHeap());
|
||||
USE(object);
|
||||
EXPECT_DEATH_IF_SUPPORTED(PreciseGC(), "");
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
@ -27,6 +27,11 @@ class TestWithHeap : public TestWithPlatform {
|
||||
protected:
|
||||
TestWithHeap();
|
||||
|
||||
void PreciseGC() {
|
||||
internal::Heap::From(GetHeap())->CollectGarbage(
|
||||
{internal::Heap::GCConfig::StackState::kEmpty});
|
||||
}
|
||||
|
||||
Heap* GetHeap() const { return heap_.get(); }
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user