| // Copyright 2016 The Chromium 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_MEMBER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_MEMBER_H_ |
| |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/heap/heap_buildflags.h" |
| #include "third_party/blink/renderer/platform/heap/impl/heap_page.h" |
| #include "third_party/blink/renderer/platform/heap/impl/marking_visitor.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| #include "third_party/blink/renderer/platform/wtf/construct_traits.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_functions.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_traits.h" |
| |
| namespace WTF { |
| template <typename P, typename Traits, typename Allocator> |
| class MemberConstructTraits; |
| } // namespace WTF |
| |
| namespace blink { |
| |
| template <typename T> |
| class Persistent; |
| |
| enum class TracenessMemberConfiguration { |
| kTraced, |
| kUntraced, |
| }; |
| |
| template <typename T, |
| TracenessMemberConfiguration tracenessConfiguration = |
| TracenessMemberConfiguration::kTraced> |
| class MemberPointerVerifier { |
| public: |
| MemberPointerVerifier() = default; |
| |
| void SaveCreationThreadState(T* pointer) { |
| if (tracenessConfiguration == TracenessMemberConfiguration::kUntraced) { |
| creation_thread_state_ = nullptr; |
| } else { |
| creation_thread_state_ = ThreadState::Current(); |
| // Members should be created in an attached thread. But an empty |
| // value Member may be created on an unattached thread by a heap |
| // collection iterator. |
| DCHECK(creation_thread_state_ || !pointer); |
| } |
| } |
| |
| void CheckPointer(T* pointer) { |
| if (!pointer) |
| return; |
| |
| ThreadState* current = ThreadState::Current(); |
| DCHECK(current); |
| if (tracenessConfiguration != TracenessMemberConfiguration::kUntraced) { |
| // creation_thread_state_ may be null when this is used in a heap |
| // collection which initialized the Member with memset and the |
| // constructor wasn't called. |
| if (creation_thread_state_) { |
| // Member should point to objects that belong in the same ThreadHeap. |
| DCHECK(creation_thread_state_->IsOnThreadHeap(pointer)); |
| // Member should point to objects that belong in the same ThreadHeap. |
| DCHECK_EQ(¤t->Heap(), &creation_thread_state_->Heap()); |
| } else { |
| DCHECK(current->IsOnThreadHeap(pointer)); |
| } |
| } |
| |
| if (current->IsSweepingInProgress()) { |
| // During sweeping the object start bitmap is invalid. Check the header |
| // when the type is available and not pointing to a mixin. |
| if (IsFullyDefined<T>::value && !IsGarbageCollectedMixin<T>::value) |
| HeapObjectHeader::CheckFromPayload(pointer); |
| } else { |
| DCHECK(HeapObjectHeader::FromInnerAddress< |
| HeapObjectHeader::AccessMode::kAtomic>(pointer)); |
| } |
| } |
| |
| private: |
| const ThreadState* creation_thread_state_; |
| }; |
| |
| template <typename T, |
| TracenessMemberConfiguration tracenessConfiguration = |
| TracenessMemberConfiguration::kTraced> |
| class MemberBase { |
| DISALLOW_NEW(); |
| |
| public: |
| MemberBase() : raw_(nullptr) { SaveCreationThreadState(); } |
| |
| MemberBase(std::nullptr_t) : raw_(nullptr) { SaveCreationThreadState(); } |
| |
| explicit MemberBase(T* raw) : raw_(raw) { |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| explicit MemberBase(T& raw) : raw_(&raw) { |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| MemberBase(WTF::HashTableDeletedValueType) |
| : raw_(reinterpret_cast<T*>(kHashTableDeletedRawValue)) { |
| SaveCreationThreadState(); |
| } |
| |
| MemberBase(const MemberBase& other) : raw_(other) { |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| template <typename U> |
| MemberBase(const Persistent<U>& other) : raw_(other) { |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| template <typename U> |
| MemberBase(const MemberBase<U>& other) : raw_(other) { |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| template <typename U> |
| MemberBase& operator=(const Persistent<U>& other) { |
| SetRaw(other); |
| CheckPointer(); |
| WriteBarrier(); |
| return *this; |
| } |
| |
| MemberBase& operator=(const MemberBase& other) { |
| SetRaw(other); |
| CheckPointer(); |
| WriteBarrier(); |
| return *this; |
| } |
| |
| template <typename U> |
| MemberBase& operator=(const MemberBase<U>& other) { |
| SetRaw(other); |
| CheckPointer(); |
| WriteBarrier(); |
| return *this; |
| } |
| |
| template <typename U> |
| MemberBase& operator=(U* other) { |
| SetRaw(other); |
| CheckPointer(); |
| WriteBarrier(); |
| return *this; |
| } |
| |
| MemberBase& operator=(WTF::HashTableDeletedValueType) { |
| SetRaw(reinterpret_cast<T*>(-1)); |
| return *this; |
| } |
| |
| MemberBase& operator=(std::nullptr_t) { |
| SetRaw(nullptr); |
| return *this; |
| } |
| |
| void Swap(MemberBase<T>& other) { |
| T* tmp = GetRaw(); |
| SetRaw(other.GetRaw()); |
| other.SetRaw(tmp); |
| CheckPointer(); |
| WriteBarrier(); |
| other.WriteBarrier(); |
| } |
| |
| explicit operator bool() const { return GetRaw(); } |
| operator T*() const { return GetRaw(); } |
| T* operator->() const { return GetRaw(); } |
| T& operator*() const { return *GetRaw(); } |
| |
| T* Get() const { return GetRaw(); } |
| |
| void Clear() { SetRaw(nullptr); } |
| |
| T* Release() { |
| T* result = GetRaw(); |
| SetRaw(nullptr); |
| return result; |
| } |
| |
| static bool IsMemberHashTableDeletedValue(const T* t) { |
| return t == reinterpret_cast<T*>(kHashTableDeletedRawValue); |
| } |
| |
| bool IsHashTableDeletedValue() const { |
| return IsMemberHashTableDeletedValue(GetRaw()); |
| } |
| |
| protected: |
| static constexpr intptr_t kHashTableDeletedRawValue = -1; |
| |
| enum class AtomicCtorTag { Atomic }; |
| |
| // MemberBase ctors that use atomic write to set raw_. |
| |
| MemberBase(AtomicCtorTag, T* raw) { |
| SetRaw(raw); |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| MemberBase(AtomicCtorTag, T& raw) { |
| SetRaw(&raw); |
| SaveCreationThreadState(); |
| CheckPointer(); |
| // No write barrier for initializing stores. |
| } |
| |
| void WriteBarrier() const { |
| MarkingVisitor::WriteBarrier( |
| reinterpret_cast<void**>(const_cast<std::remove_const_t<T>**>(&raw_))); |
| } |
| |
| void CheckPointer() { |
| #if DCHECK_IS_ON() |
| // Should not be called for deleted hash table values. A value can be |
| // propagated here if a MemberBase containing the deleted value is copied. |
| if (IsHashTableDeletedValue()) |
| return; |
| pointer_verifier_.CheckPointer(GetRaw()); |
| #endif // DCHECK_IS_ON() |
| } |
| |
| void SaveCreationThreadState() { |
| #if DCHECK_IS_ON() |
| pointer_verifier_.SaveCreationThreadState(GetRaw()); |
| #endif // DCHECK_IS_ON() |
| } |
| |
| ALWAYS_INLINE void SetRaw(T* raw) { |
| if (tracenessConfiguration == TracenessMemberConfiguration::kUntraced) |
| raw_ = raw; |
| else |
| WTF::AsAtomicPtr(&raw_)->store(raw, std::memory_order_relaxed); |
| } |
| ALWAYS_INLINE T* GetRaw() const { return raw_; } |
| |
| private: |
| // Thread safe version of Get() for marking visitors. |
| // This is used to prevent data races between concurrent marking visitors |
| // and writes on the main thread. |
| const T* GetSafe() const { |
| // TOOD(omerkatz): replace this cast with std::atomic_ref (C++20) once it |
| // becomes available |
| return WTF::AsAtomicPtr(&raw_)->load(std::memory_order_relaxed); |
| } |
| |
| T* raw_; |
| #if DCHECK_IS_ON() |
| MemberPointerVerifier<T, tracenessConfiguration> pointer_verifier_; |
| #endif // DCHECK_IS_ON() |
| |
| friend class Visitor; |
| }; |
| |
| // Members are used in classes to contain strong pointers to other oilpan heap |
| // allocated objects. |
| // All Member fields of a class must be traced in the class' trace method. |
| // During the mark phase of the GC all live objects are marked as live and |
| // all Member fields of a live object will be traced marked as live as well. |
| template <typename T> |
| class Member : public MemberBase<T, TracenessMemberConfiguration::kTraced> { |
| DISALLOW_NEW(); |
| typedef MemberBase<T, TracenessMemberConfiguration::kTraced> Parent; |
| |
| public: |
| Member() : Parent() {} |
| Member(std::nullptr_t) : Parent(nullptr) {} |
| Member(T* raw) : Parent(raw) {} |
| Member(T& raw) : Parent(raw) {} |
| Member(WTF::HashTableDeletedValueType x) : Parent(x) {} |
| |
| Member(const Member& other) : Parent(other) {} |
| |
| template <typename U> |
| Member(const Member<U>& other) : Parent(other) {} |
| |
| template <typename U> |
| Member(const Persistent<U>& other) : Parent(other) {} |
| |
| template <typename U> |
| Member& operator=(const Persistent<U>& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| Member& operator=(const Member& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| template <typename U> |
| Member& operator=(const Member<U>& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| template <typename U> |
| Member& operator=(const WeakMember<U>& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| template <typename U> |
| Member& operator=(U* other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| Member& operator=(WTF::HashTableDeletedValueType x) { |
| Parent::operator=(x); |
| return *this; |
| } |
| |
| Member& operator=(std::nullptr_t) { |
| Parent::operator=(nullptr); |
| return *this; |
| } |
| |
| private: |
| using typename Parent::AtomicCtorTag; |
| Member(AtomicCtorTag atomic, T* raw) : Parent(atomic, raw) {} |
| Member(AtomicCtorTag atomic, T& raw) : Parent(atomic, raw) {} |
| |
| template <typename P, typename Traits, typename Allocator> |
| friend class WTF::MemberConstructTraits; |
| }; |
| |
| // WeakMember is similar to Member in that it is used to point to other oilpan |
| // heap allocated objects. |
| // However instead of creating a strong pointer to the object, the WeakMember |
| // creates a weak pointer, which does not keep the pointee alive. Hence if all |
| // pointers to a heap allocated object are weak the object will be garbage |
| // collected. At the time of GC the weak pointers will automatically be set to |
| // null. |
| template <typename T> |
| class WeakMember : public MemberBase<T, TracenessMemberConfiguration::kTraced> { |
| typedef MemberBase<T, TracenessMemberConfiguration::kTraced> Parent; |
| |
| public: |
| WeakMember() : Parent() {} |
| |
| WeakMember(std::nullptr_t) : Parent(nullptr) {} |
| |
| WeakMember(T* raw) : Parent(raw) {} |
| |
| WeakMember(WTF::HashTableDeletedValueType x) : Parent(x) {} |
| |
| template <typename U> |
| WeakMember(const Persistent<U>& other) : Parent(other) {} |
| |
| template <typename U> |
| WeakMember(const Member<U>& other) : Parent(other) {} |
| |
| template <typename U> |
| WeakMember& operator=(const Persistent<U>& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| template <typename U> |
| WeakMember& operator=(const Member<U>& other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| template <typename U> |
| WeakMember& operator=(U* other) { |
| Parent::operator=(other); |
| return *this; |
| } |
| |
| WeakMember& operator=(std::nullptr_t) { |
| this->SetRaw(nullptr); |
| return *this; |
| } |
| |
| private: |
| using typename Parent::AtomicCtorTag; |
| WeakMember(AtomicCtorTag atomic, T* raw) : Parent(atomic, raw) {} |
| WeakMember(AtomicCtorTag atomic, T& raw) : Parent(atomic, raw) {} |
| |
| template <typename P, typename Traits, typename Allocator> |
| friend class WTF::MemberConstructTraits; |
| }; |
| |
| // UntracedMember is a pointer to an on-heap object that is not traced for some |
| // reason. Please don't use this unless you understand what you're doing. |
| // Basically, all pointers to on-heap objects must be stored in either of |
| // Persistent, Member or WeakMember. It is not allowed to leave raw pointers to |
| // on-heap objects. However, there can be scenarios where you have to use raw |
| // pointers for some reason, and in that case you can use UntracedMember. Of |
| // course, it must be guaranteed that the pointing on-heap object is kept alive |
| // while the raw pointer is pointing to the object. |
| template <typename T> |
| class UntracedMember final |
| : public MemberBase<T, TracenessMemberConfiguration::kUntraced> { |
| typedef MemberBase<T, TracenessMemberConfiguration::kUntraced> Parent; |
| |
| public: |
| UntracedMember() : Parent() {} |
| |
| UntracedMember(std::nullptr_t) : Parent(nullptr) {} |
| |
| UntracedMember(T* raw) : Parent(raw) {} |
| |
| template <typename U> |
| UntracedMember(const Persistent<U>& other) : Parent(other) {} |
| |
| template <typename U> |
| UntracedMember(const Member<U>& other) : Parent(other) {} |
| |
| UntracedMember(WTF::HashTableDeletedValueType x) : Parent(x) {} |
| |
| UntracedMember& operator=(const UntracedMember& other) { |
| this->SetRaw(other); |
| this->CheckPointer(); |
| return *this; |
| } |
| |
| template <typename U> |
| UntracedMember& operator=(const Persistent<U>& other) { |
| this->SetRaw(other); |
| this->CheckPointer(); |
| return *this; |
| } |
| |
| template <typename U> |
| UntracedMember& operator=(const Member<U>& other) { |
| this->SetRaw(other); |
| this->CheckPointer(); |
| return *this; |
| } |
| |
| template <typename U> |
| UntracedMember& operator=(U* other) { |
| this->SetRaw(other); |
| this->CheckPointer(); |
| return *this; |
| } |
| |
| UntracedMember& operator=(std::nullptr_t) { |
| this->SetRaw(nullptr); |
| return *this; |
| } |
| }; |
| |
| template <typename T> |
| struct MemberTraceTraits { |
| STATIC_ONLY(MemberTraceTraits); |
| |
| public: |
| static TraceDescriptor GetTraceDescriptor(const T* ref) { |
| return {ref, TraceTrait<T>::Trace}; |
| } |
| |
| static void Trace(Visitor* visitor, const void* ref) { |
| visitor->Trace(*static_cast<const T*>(ref)); |
| } |
| }; |
| |
| template <typename T> |
| struct TraceTrait<Member<T>> : public MemberTraceTraits<Member<T>> {}; |
| |
| template <typename T> |
| struct TraceTrait<WeakMember<T>> : public MemberTraceTraits<WeakMember<T>> {}; |
| |
| template <typename T> |
| inline bool IsHashTableDeletedValue(const Member<T>& m) { |
| return m.IsHashTableDeletedValue(); |
| } |
| |
| constexpr auto kMemberDeletedValue = WTF::kHashTableDeletedValue; |
| |
| } // namespace blink |
| |
| namespace WTF { |
| |
| template <typename T, typename Traits, typename Allocator> |
| class MemberConstructTraits { |
| STATIC_ONLY(MemberConstructTraits); |
| |
| public: |
| template <typename... Args> |
| static T* Construct(void* location, Args&&... args) { |
| return new (NotNull, location) T(std::forward<Args>(args)...); |
| } |
| |
| static void NotifyNewElement(T* element) { element->WriteBarrier(); } |
| |
| template <typename... Args> |
| static T* ConstructAndNotifyElement(void* location, Args&&... args) { |
| // ConstructAndNotifyElement updates an existing Member which might |
| // also be comncurrently traced while we update it. The regular ctors |
| // for Member don't use an atomic write which can lead to data races. |
| T* object = Construct(location, T::AtomicCtorTag::Atomic, |
| std::forward<Args>(args)...); |
| NotifyNewElement(object); |
| return object; |
| } |
| |
| static void NotifyNewElements(T* array, size_t len) { |
| while (len-- > 0) { |
| array->WriteBarrier(); |
| array++; |
| } |
| } |
| }; |
| |
| template <typename T, typename Traits, typename Allocator> |
| class ConstructTraits<blink::Member<T>, Traits, Allocator> |
| : public MemberConstructTraits<blink::Member<T>, Traits, Allocator> {}; |
| |
| template <typename T, typename Traits, typename Allocator> |
| class ConstructTraits<blink::WeakMember<T>, Traits, Allocator> |
| : public MemberConstructTraits<blink::WeakMember<T>, Traits, Allocator> {}; |
| |
| } // namespace WTF |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_MEMBER_H_ |