Commit e18d8765 authored by Anton Bikineev's avatar Anton Bikineev Committed by Commit Bot

heap: Use std::is_trivially_destructible<> to determine finalized types

After this commit the GarbageCollectedFinalized class becomes a dumb
class. It will be removed as soon as the blink-gc-plugin is updated
and clang is rolled.

Change-Id: I6c6454bf434f1daac0ee3b741995113c3e68c15e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1738378
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: default avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695180}
parent 5cb0eb17
...@@ -40,8 +40,13 @@ You may not allocate an on-heap object on stack. ...@@ -40,8 +40,13 @@ You may not allocate an on-heap object on stack.
Your class may need to have a tracing method. See [Tracing](#Tracing) for details. Your class may need to have a tracing method. See [Tracing](#Tracing) for details.
If your class needs finalization (i.e. some work needs to be done on destruction), use Your class will be automatically finalized as long as it is non-trivially destructible. Non-final classes are
[GarbageCollectedFinalized](#GarbageCollectedFinalized) instead. required to have a virtual destructor.
Note that finalization is done at an arbitrary time after the object becomes unreachable.
Any destructor executed within the finalization period must not touch any other on-heap object, because destructors
can be executed in any order.
`GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first `GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first
element in its base class list (called "leftmost derivation rule"). This rule is needed to assure each on-heap object element in its base class list (called "leftmost derivation rule"). This rule is needed to assure each on-heap object
...@@ -75,40 +80,11 @@ public: ...@@ -75,40 +80,11 @@ public:
}; };
``` ```
### GarbageCollectedFinalized
If you want to make your class garbage-collected and the class needs finalization, your class needs to inherit from
`GarbageCollectedFinalized<YourClass>` instead of `GarbageCollected<YourClass>`.
A class is said to *need finalization* when it meets either of the following criteria:
* It has non-empty destructor; or
* It has a member that needs finalization.
```c++
class YourClass : public GarbageCollectedFinalized<YourClass> {
public:
~YourClass() { ... } // Non-empty destructor means finalization is needed.
private:
scoped_refptr<Something> something_; // scoped_refptr<> has non-empty destructor, so finalization is needed.
};
```
Note that finalization is done at an arbitrary time after the object becomes unreachable.
Any destructor executed within the finalization period must not touch any other on-heap object, because destructors
can be executed in any order.
Because `GarbageCollectedFinalized<T>` is a special case of `GarbageCollected<T>`, all the restrictions that apply
to `GarbageCollected<T>` classes also apply to `GarbageCollectedFinalized<T>`.
### GarbageCollectedMixin ### GarbageCollectedMixin
A non-leftmost base class of a garbage-collected class may derive from `GarbageCollectedMixin`. If a direct child A non-leftmost base class of a garbage-collected class may derive from `GarbageCollectedMixin`. If a direct child
class of `GarbageCollected<T>` or `GarbageCollectedFinalized<T>` has a non-leftmost base class deriving from class of `GarbageCollected<T>` has a non-leftmost base class deriving from `GarbageCollectedMixin`, the
`GarbageCollectedMixin`, the garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro in its class declaration.
in its class declaration.
A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes. Specifically, it A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes. Specifically, it
can have `Member<T>`s and `WeakMember<T>`s, and a tracing method. A pointer to such a class must be retained in the can have `Member<T>`s and `WeakMember<T>`s, and a tracing method. A pointer to such a class must be retained in the
...@@ -179,7 +155,7 @@ It is invoked before the sweeping phase starts to allow a pre-finalizer to touch ...@@ -179,7 +155,7 @@ It is invoked before the sweeping phase starts to allow a pre-finalizer to touch
It is useful for doing cleanups that cannot be done with a destructor. It is useful for doing cleanups that cannot be done with a destructor.
```c++ ```c++
class YourClass : public GarbageCollectedFinalized<YourClass> { class YourClass : public GarbageCollected<YourClass> {
USING_PRE_FINALIZER(YourClass, Dispose); USING_PRE_FINALIZER(YourClass, Dispose);
public: public:
void Dispose() { void Dispose() {
...@@ -200,7 +176,7 @@ Sometimes it is necessary to further delegate pre-finalizers up the class hierar ...@@ -200,7 +176,7 @@ Sometimes it is necessary to further delegate pre-finalizers up the class hierar
It is possible to construct such delegations using virtual methods. It is possible to construct such delegations using virtual methods.
```c++ ```c++
class Parent : public GarbageCollectedFinalized<Parent> { class Parent : public GarbageCollected<Parent> {
USING_PRE_FINALIZER(Parent, Dispose); USING_PRE_FINALIZER(Parent, Dispose);
public: public:
void Dispose() { DisposeImpl(); } void Dispose() { DisposeImpl(); }
......
...@@ -30,20 +30,31 @@ struct HasFinalizeGarbageCollectedObject< ...@@ -30,20 +30,31 @@ struct HasFinalizeGarbageCollectedObject<
base::void_t<decltype(std::declval<T>().FinalizeGarbageCollectedObject())>> base::void_t<decltype(std::declval<T>().FinalizeGarbageCollectedObject())>>
: std::true_type {}; : std::true_type {};
} // namespace internal // The FinalizerTraitImpl specifies how to finalize objects.
// The FinalizerTraitImpl specifies how to finalize objects. Objects that
// inherit from GarbageCollectedFinalized are finalized by calling their
// |Finalize| method which by default will call the destructor on the object.
template <typename T, bool isGarbageCollectedFinalized> template <typename T, bool isGarbageCollectedFinalized>
struct FinalizerTraitImpl; struct FinalizerTraitImpl;
template <typename T> template <typename T>
struct FinalizerTraitImpl<T, true> { struct FinalizerTraitImpl<T, true> {
private:
STATIC_ONLY(FinalizerTraitImpl); STATIC_ONLY(FinalizerTraitImpl);
struct CustomDispatch {
static void Call(void* obj) {
static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
}
};
struct DestructorDispatch {
static void Call(void* obj) { static_cast<T*>(obj)->~T(); }
};
using FinalizeImpl =
std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value,
CustomDispatch,
DestructorDispatch>;
public:
static void Finalize(void* obj) { static void Finalize(void* obj) {
static_assert(sizeof(T), "T must be fully defined"); static_assert(sizeof(T), "T must be fully defined");
static_cast<T*>(obj)->FinalizeGarbageCollectedObject(); FinalizeImpl::Call(obj);
} }
}; };
...@@ -55,21 +66,18 @@ struct FinalizerTraitImpl<T, false> { ...@@ -55,21 +66,18 @@ struct FinalizerTraitImpl<T, false> {
} }
}; };
} // namespace internal
// The FinalizerTrait is used to determine if a type requires finalization and // The FinalizerTrait is used to determine if a type requires finalization and
// what finalization means. // what finalization means.
//
// By default classes that inherit from GarbageCollectedFinalized need
// finalization and finalization means calling the |Finalize| method of the
// object. The FinalizerTrait can be specialized if the default behavior is not
// desired.
template <typename T> template <typename T>
struct FinalizerTrait { struct FinalizerTrait {
STATIC_ONLY(FinalizerTrait); STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = static constexpr bool kNonTrivialFinalizer =
WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type, internal::HasFinalizeGarbageCollectedObject<T>::value ||
GarbageCollectedFinalized>::value; !std::is_trivially_destructible<typename std::remove_cv<T>::type>::value;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj); internal::FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
...@@ -82,32 +90,32 @@ class HeapHashTableBacking; ...@@ -82,32 +90,32 @@ class HeapHashTableBacking;
template <typename T, typename U, typename V> template <typename T, typename U, typename V>
struct FinalizerTrait<LinkedHashSet<T, U, V, HeapAllocator>> { struct FinalizerTrait<LinkedHashSet<T, U, V, HeapAllocator>> {
STATIC_ONLY(FinalizerTrait); STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = true; static constexpr bool kNonTrivialFinalizer = true;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<LinkedHashSet<T, U, V, HeapAllocator>, internal::FinalizerTraitImpl<LinkedHashSet<T, U, V, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
template <typename T, typename Allocator> template <typename T, typename Allocator>
struct FinalizerTrait<WTF::ListHashSetNode<T, Allocator>> { struct FinalizerTrait<WTF::ListHashSetNode<T, Allocator>> {
STATIC_ONLY(FinalizerTrait); STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = static constexpr bool kNonTrivialFinalizer =
!std::is_trivially_destructible<T>::value; !std::is_trivially_destructible<T>::value;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<WTF::ListHashSetNode<T, Allocator>, internal::FinalizerTraitImpl<WTF::ListHashSetNode<T, Allocator>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
template <typename T, size_t inlineCapacity> template <typename T, size_t inlineCapacity>
struct FinalizerTrait<Vector<T, inlineCapacity, HeapAllocator>> { struct FinalizerTrait<Vector<T, inlineCapacity, HeapAllocator>> {
STATIC_ONLY(FinalizerTrait); STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = static constexpr bool kNonTrivialFinalizer =
inlineCapacity && VectorTraits<T>::kNeedsDestruction; inlineCapacity && VectorTraits<T>::kNeedsDestruction;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<Vector<T, inlineCapacity, HeapAllocator>, internal::FinalizerTraitImpl<Vector<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
...@@ -117,8 +125,8 @@ struct FinalizerTrait<Deque<T, inlineCapacity, HeapAllocator>> { ...@@ -117,8 +125,8 @@ struct FinalizerTrait<Deque<T, inlineCapacity, HeapAllocator>> {
static const bool kNonTrivialFinalizer = static const bool kNonTrivialFinalizer =
inlineCapacity && VectorTraits<T>::kNeedsDestruction; inlineCapacity && VectorTraits<T>::kNeedsDestruction;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<Deque<T, inlineCapacity, HeapAllocator>, internal::FinalizerTraitImpl<Deque<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
...@@ -128,8 +136,8 @@ struct FinalizerTrait<HeapHashTableBacking<Table>> { ...@@ -128,8 +136,8 @@ struct FinalizerTrait<HeapHashTableBacking<Table>> {
static const bool kNonTrivialFinalizer = static const bool kNonTrivialFinalizer =
!std::is_trivially_destructible<typename Table::ValueType>::value; !std::is_trivially_destructible<typename Table::ValueType>::value;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<HeapHashTableBacking<Table>, internal::FinalizerTraitImpl<HeapHashTableBacking<Table>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
...@@ -138,8 +146,8 @@ struct FinalizerTrait<HeapVectorBacking<T, Traits>> { ...@@ -138,8 +146,8 @@ struct FinalizerTrait<HeapVectorBacking<T, Traits>> {
STATIC_ONLY(FinalizerTrait); STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction; static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction;
static void Finalize(void* obj) { static void Finalize(void* obj) {
FinalizerTraitImpl<HeapVectorBacking<T, Traits>, internal::FinalizerTraitImpl<HeapVectorBacking<T, Traits>,
kNonTrivialFinalizer>::Finalize(obj); kNonTrivialFinalizer>::Finalize(obj);
} }
}; };
......
...@@ -182,41 +182,16 @@ class PLATFORM_EXPORT GarbageCollectedMixin { ...@@ -182,41 +182,16 @@ class PLATFORM_EXPORT GarbageCollectedMixin {
// Defines a 'new' operator that allocates the memory in the heap. 'delete' // Defines a 'new' operator that allocates the memory in the heap. 'delete'
// should not be called on objects that inherit from GarbageCollected. // should not be called on objects that inherit from GarbageCollected.
// //
// Instances of GarbageCollected will *NOT* get finalized. Their destructor // Instances of GarbageCollected will be finalized if they are non-trivially
// will not be called. Therefore, only classes that have trivial destructors // destructible.
// with no semantic meaning (including all their subclasses) should inherit from
// GarbageCollected. If there are non-trival destructors in a given class or
// any of its subclasses, GarbageCollectedFinalized should be used which
// guarantees that the destructor is called on an instance when the garbage
// collector determines that it is no longer reachable.
template <typename T> template <typename T>
class GarbageCollected; class GarbageCollected;
// Base class for objects allocated in the Blink garbage-collected heap. // TODO(bikineev): Remove this class after the clang plugin is updated.
//
// Defines a 'new' operator that allocates the memory in the heap. 'delete'
// should not be called on objects that inherit from GarbageCollected.
//
// Instances of GarbageCollectedFinalized will have their destructor called when
// the garbage collector determines that the object is no longer reachable.
template <typename T> template <typename T>
class GarbageCollectedFinalized : public GarbageCollected<T> { class GarbageCollectedFinalized : public GarbageCollected<T> {
protected: protected:
// finalizeGarbageCollectedObject is called when the object is freed from
// the heap. By default finalization means calling the destructor on the
// object. finalizeGarbageCollectedObject can be overridden to support
// calling the destructor of a subclass. This is useful for objects without
// vtables that require explicit dispatching. The name is intentionally a
// bit long to make name conflicts less likely.
void FinalizeGarbageCollectedObject() { static_cast<T*>(this)->~T(); }
GarbageCollectedFinalized() = default; GarbageCollectedFinalized() = default;
~GarbageCollectedFinalized() = default;
template <typename U>
friend struct HasFinalizer;
template <typename U, bool>
friend struct FinalizerTraitImpl;
DISALLOW_COPY_AND_ASSIGN(GarbageCollectedFinalized); DISALLOW_COPY_AND_ASSIGN(GarbageCollectedFinalized);
}; };
......
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