| // Copyright 2020 The Chromium Authors |
| // 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_MEMBER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MEMBER_H_ |
| |
| #include "third_party/blink/renderer/platform/heap/persistent.h" |
| #include "third_party/blink/renderer/platform/heap/thread_state_storage.h" |
| #include "third_party/blink/renderer/platform/heap/write_barrier.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_traits.h" |
| #include "third_party/blink/renderer/platform/wtf/type_traits.h" |
| #include "v8/include/cppgc/member.h" // IWYU pragma: export |
| #include "v8/include/cppgc/tagged-member.h" |
| |
| namespace blink { |
| |
| template <typename T> |
| using Member = cppgc::Member<T>; |
| |
| template <typename T> |
| using WeakMember = cppgc::WeakMember<T>; |
| |
| template <typename T> |
| using UntracedMember = cppgc::UntracedMember<T>; |
| |
| namespace subtle { |
| |
| template <typename T> |
| using UncompressedMember = cppgc::subtle::UncompressedMember<T>; |
| |
| template <typename T, typename Tag1, typename Tag2> |
| using TaggedUncompressedMember = |
| cppgc::subtle::TaggedUncompressedMember<T, Tag1, Tag2>; |
| } |
| |
| template <typename T> |
| inline bool IsHashTableDeletedValue(const Member<T>& m) { |
| return m == cppgc::kSentinelPointer; |
| } |
| |
| constexpr auto kMemberDeletedValue = cppgc::kSentinelPointer; |
| |
| template <typename T> |
| struct ThreadingTrait<blink::Member<T>> { |
| STATIC_ONLY(ThreadingTrait); |
| static constexpr ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity; |
| }; |
| |
| template <typename T> |
| struct ThreadingTrait<blink::WeakMember<T>> { |
| STATIC_ONLY(ThreadingTrait); |
| static constexpr ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity; |
| }; |
| |
| template <typename T> |
| struct ThreadingTrait<blink::UntracedMember<T>> { |
| STATIC_ONLY(ThreadingTrait); |
| static constexpr ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity; |
| }; |
| |
| template <typename T> |
| inline void swap(Member<T>& a, Member<T>& b) { |
| a.Swap(b); |
| } |
| |
| static constexpr bool kBlinkMemberGCHasDebugChecks = |
| !std::is_same<cppgc::internal::DefaultMemberCheckingPolicy, |
| cppgc::internal::DisabledCheckingPolicy>::value; |
| |
| // We should never bloat the Member<> wrapper. |
| // NOTE: The Member<void*> works as we never use this Member in a trace method. |
| static_assert(kBlinkMemberGCHasDebugChecks || |
| sizeof(Member<void*>) <= sizeof(void*), |
| "Member<> should stay small!"); |
| |
| template <typename T> |
| struct IsTraceable<Member<T>> { |
| STATIC_ONLY(IsTraceable); |
| static const bool value = true; |
| }; |
| |
| template <typename T> |
| struct IsWeak<WeakMember<T>> : std::true_type {}; |
| |
| template <typename T> |
| struct IsTraceable<WeakMember<T>> { |
| STATIC_ONLY(IsTraceable); |
| static const bool value = true; |
| }; |
| |
| // Peeker type that allows for using all kinds of Member, Persistent, and T* |
| // interchangeably. This is necessary for collection methods that are called |
| // directly with any of those types. |
| template <typename T> |
| class ValuePeeker final { |
| STACK_ALLOCATED(); |
| |
| public: |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(T* ptr) : ptr_(ptr) {} |
| template <typename U> |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(const Member<U>& m) : ptr_(m.Get()) {} |
| template <typename U> |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(const WeakMember<U>& m) : ptr_(m.Get()) {} |
| template <typename U> |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(const UntracedMember<U>& m) : ptr_(m.Get()) {} |
| template <typename U> |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(const Persistent<U>& p) : ptr_(p.Get()) {} |
| template <typename U> |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE ValuePeeker(const WeakPersistent<U>& p) : ptr_(p.Get()) {} |
| |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE operator T*() const { return ptr_; } |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE operator Member<T>() const { return ptr_; } |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE operator WeakMember<T>() const { return ptr_; } |
| // NOLINTNEXTLINE |
| ALWAYS_INLINE operator UntracedMember<T>() const { return ptr_; } |
| |
| private: |
| T* ptr_; |
| }; |
| |
| // Default hash for hash tables with Member<>-derived elements. |
| template <typename T, typename MemberType> |
| struct BaseMemberHashTraits : SimpleClassHashTraits<MemberType> { |
| STATIC_ONLY(BaseMemberHashTraits); |
| |
| // Heap hash containers allow to operate with raw pointers, e.g. |
| // HeapHashSet<Member<GCed>> set; |
| // set.find(raw_ptr); |
| // Therefore, provide two hashing functions, one for raw pointers, another for |
| // Member. Prefer compressing raw pointers instead of decompressing Members, |
| // assuming the former is cheaper. |
| static unsigned GetHash(const T* key) { |
| #if defined(CPPGC_POINTER_COMPRESSION) |
| cppgc::internal::CompressedPointer st(key); |
| #else |
| cppgc::internal::RawPointer st(key); |
| #endif |
| return blink::GetHash(st.GetAsInteger()); |
| } |
| template <typename Member> |
| requires(IsAnyMemberType<Member>::value) |
| static unsigned GetHash(const Member& m) { |
| return blink::GetHash(m.GetRawStorage().GetAsInteger()); |
| } |
| |
| static constexpr bool kEmptyValueIsZero = true; |
| |
| using PeekInType = ValuePeeker<T>; |
| using PeekOutType = T*; |
| using IteratorGetType = MemberType*; |
| using IteratorConstGetType = const MemberType*; |
| using IteratorReferenceType = MemberType&; |
| using IteratorConstReferenceType = const MemberType&; |
| |
| static PeekOutType Peek(const MemberType& value) { return value.Get(); } |
| |
| static void ConstructDeletedValue(MemberType& slot) { |
| slot = cppgc::kSentinelPointer; |
| } |
| |
| static bool IsDeletedValue(const MemberType& value) { |
| return value == cppgc::kSentinelPointer; |
| } |
| }; |
| |
| // Custom HashTraits<Member<Type>> can inherit this type. |
| template <typename T> |
| struct MemberHashTraits : BaseMemberHashTraits<T, blink::Member<T>> { |
| static constexpr bool kCanTraceConcurrently = true; |
| static constexpr bool kSupportsCompaction = true; |
| }; |
| template <typename T> |
| struct HashTraits<blink::Member<T>> : MemberHashTraits<T> {}; |
| |
| // Custom HashTraits<WeakMember<Type>> can inherit this type. |
| template <typename T> |
| struct WeakMemberHashTraits : BaseMemberHashTraits<T, blink::WeakMember<T>> { |
| static constexpr bool kCanTraceConcurrently = true; |
| static constexpr bool kSupportsCompaction = true; |
| }; |
| template <typename T> |
| struct HashTraits<blink::WeakMember<T>> : WeakMemberHashTraits<T> {}; |
| |
| // Custom HashTraits<UntracedMember<Type>> can inherit this type. |
| template <typename T> |
| struct UntracedMemberHashTraits |
| : BaseMemberHashTraits<T, blink::UntracedMember<T>> {}; |
| template <typename T> |
| struct HashTraits<blink::UntracedMember<T>> : UntracedMemberHashTraits<T> {}; |
| |
| template <typename T> |
| class MemberConstructTraits { |
| STATIC_ONLY(MemberConstructTraits); |
| |
| public: |
| template <typename... Args> |
| static T* Construct(void* location, Args&&... args) { |
| // `Construct()` creates a new Member which must not be visible to the |
| // concurrent marker yet, similar to regular ctors in Member. |
| return new (base::NotNullTag::kNotNull, location) |
| T(std::forward<Args>(args)...); |
| } |
| |
| template <typename... Args> |
| static T* ConstructAndNotifyElement(void* location, Args&&... args) { |
| // `ConstructAndNotifyElement()` updates an existing Member which might |
| // also be concurrently traced while we update it. The regular ctors |
| // for Member don't use an atomic write which can lead to data races. |
| T* object = new (base::NotNullTag::kNotNull, location) |
| T(std::forward<Args>(args)..., typename T::AtomicInitializerTag()); |
| NotifyNewElement(object); |
| return object; |
| } |
| |
| static void NotifyNewElement(T* element) { |
| WriteBarrier::DispatchForObject(element); |
| } |
| |
| static void NotifyNewElements(base::span<T> members) { |
| // Checking the first element is sufficient for determining whether a |
| // marking or generational barrier is required. |
| if (members.empty() || |
| !WriteBarrier::IsWriteBarrierNeeded(&members.front())) [[likely]] { |
| return; |
| } |
| for (auto& member : members) { |
| WriteBarrier::DispatchForObject(&member); |
| } |
| } |
| }; |
| |
| template <typename T, typename Traits, typename Allocator> |
| class ConstructTraits<Member<T>, Traits, Allocator> final |
| : public MemberConstructTraits<Member<T>> {}; |
| |
| template <typename T, typename Traits, typename Allocator> |
| class ConstructTraits<WeakMember<T>, Traits, Allocator> final |
| : public MemberConstructTraits<WeakMember<T>> {}; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MEMBER_H_ |