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.
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
[GarbageCollectedFinalized](#GarbageCollectedFinalized) instead.
Your class will be automatically finalized as long as it is non-trivially destructible. Non-final classes are
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
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:
};
```
### 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
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
`GarbageCollectedMixin`, the garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro
in its class declaration.
class of `GarbageCollected<T>` has a non-leftmost base class deriving from `GarbageCollectedMixin`, the
garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro in its class declaration.
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
......@@ -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.
```c++
class YourClass : public GarbageCollectedFinalized<YourClass> {
class YourClass : public GarbageCollected<YourClass> {
USING_PRE_FINALIZER(YourClass, Dispose);
public:
void Dispose() {
......@@ -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.
```c++
class Parent : public GarbageCollectedFinalized<Parent> {
class Parent : public GarbageCollected<Parent> {
USING_PRE_FINALIZER(Parent, Dispose);
public:
void Dispose() { DisposeImpl(); }
......
......@@ -30,20 +30,31 @@ struct HasFinalizeGarbageCollectedObject<
base::void_t<decltype(std::declval<T>().FinalizeGarbageCollectedObject())>>
: std::true_type {};
} // namespace internal
// 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.
// The FinalizerTraitImpl specifies how to finalize objects.
template <typename T, bool isGarbageCollectedFinalized>
struct FinalizerTraitImpl;
template <typename T>
struct FinalizerTraitImpl<T, true> {
private:
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_assert(sizeof(T), "T must be fully defined");
static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
FinalizeImpl::Call(obj);
}
};
......@@ -55,21 +66,18 @@ struct FinalizerTraitImpl<T, false> {
}
};
} // namespace internal
// The FinalizerTrait is used to determine if a type requires finalization and
// 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>
struct FinalizerTrait {
STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer =
WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
GarbageCollectedFinalized>::value;
static constexpr bool kNonTrivialFinalizer =
internal::HasFinalizeGarbageCollectedObject<T>::value ||
!std::is_trivially_destructible<typename std::remove_cv<T>::type>::value;
static void Finalize(void* obj) {
FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
}
};
......@@ -82,32 +90,32 @@ class HeapHashTableBacking;
template <typename T, typename U, typename V>
struct FinalizerTrait<LinkedHashSet<T, U, V, HeapAllocator>> {
STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = true;
static constexpr bool kNonTrivialFinalizer = true;
static void Finalize(void* obj) {
FinalizerTraitImpl<LinkedHashSet<T, U, V, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<LinkedHashSet<T, U, V, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
template <typename T, typename Allocator>
struct FinalizerTrait<WTF::ListHashSetNode<T, Allocator>> {
STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer =
static constexpr bool kNonTrivialFinalizer =
!std::is_trivially_destructible<T>::value;
static void Finalize(void* obj) {
FinalizerTraitImpl<WTF::ListHashSetNode<T, Allocator>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<WTF::ListHashSetNode<T, Allocator>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
template <typename T, size_t inlineCapacity>
struct FinalizerTrait<Vector<T, inlineCapacity, HeapAllocator>> {
STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer =
static constexpr bool kNonTrivialFinalizer =
inlineCapacity && VectorTraits<T>::kNeedsDestruction;
static void Finalize(void* obj) {
FinalizerTraitImpl<Vector<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<Vector<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
......@@ -117,8 +125,8 @@ struct FinalizerTrait<Deque<T, inlineCapacity, HeapAllocator>> {
static const bool kNonTrivialFinalizer =
inlineCapacity && VectorTraits<T>::kNeedsDestruction;
static void Finalize(void* obj) {
FinalizerTraitImpl<Deque<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<Deque<T, inlineCapacity, HeapAllocator>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
......@@ -128,8 +136,8 @@ struct FinalizerTrait<HeapHashTableBacking<Table>> {
static const bool kNonTrivialFinalizer =
!std::is_trivially_destructible<typename Table::ValueType>::value;
static void Finalize(void* obj) {
FinalizerTraitImpl<HeapHashTableBacking<Table>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<HeapHashTableBacking<Table>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
......@@ -138,8 +146,8 @@ struct FinalizerTrait<HeapVectorBacking<T, Traits>> {
STATIC_ONLY(FinalizerTrait);
static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction;
static void Finalize(void* obj) {
FinalizerTraitImpl<HeapVectorBacking<T, Traits>,
kNonTrivialFinalizer>::Finalize(obj);
internal::FinalizerTraitImpl<HeapVectorBacking<T, Traits>,
kNonTrivialFinalizer>::Finalize(obj);
}
};
......
......@@ -182,41 +182,16 @@ class PLATFORM_EXPORT GarbageCollectedMixin {
// Defines a 'new' operator that allocates the memory in the heap. 'delete'
// should not be called on objects that inherit from GarbageCollected.
//
// Instances of GarbageCollected will *NOT* get finalized. Their destructor
// will not be called. Therefore, only classes that have trivial destructors
// 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.
// Instances of GarbageCollected will be finalized if they are non-trivially
// destructible.
template <typename T>
class GarbageCollected;
// Base class for objects allocated in the Blink garbage-collected heap.
//
// 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.
// TODO(bikineev): Remove this class after the clang plugin is updated.
template <typename T>
class GarbageCollectedFinalized : public GarbageCollected<T> {
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;
template <typename U>
friend struct HasFinalizer;
template <typename U, bool>
friend struct FinalizerTraitImpl;
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