1 // Copyright 2020 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef INCLUDE_CPPGC_MEMBER_H_ 6 #define INCLUDE_CPPGC_MEMBER_H_ 7 8 #include <atomic> 9 #include <cstddef> 10 #include <type_traits> 11 12 #include "cppgc/internal/pointer-policies.h" 13 #include "cppgc/sentinel-pointer.h" 14 #include "cppgc/type-traits.h" 15 #include "v8config.h" // NOLINT(build/include_directory) 16 17 namespace cppgc { 18 19 class Visitor; 20 21 namespace internal { 22 23 // MemberBase always refers to the object as const object and defers to 24 // BasicMember on casting to the right type as needed. 25 class MemberBase { 26 protected: 27 struct AtomicInitializerTag {}; 28 MemberBase()29 MemberBase() : raw_(nullptr) {} MemberBase(const void * value)30 explicit MemberBase(const void* value) : raw_(value) {} MemberBase(const void * value,AtomicInitializerTag)31 MemberBase(const void* value, AtomicInitializerTag) { SetRawAtomic(value); } 32 GetRawSlot()33 const void** GetRawSlot() const { return &raw_; } GetRaw()34 const void* GetRaw() const { return raw_; } SetRaw(void * value)35 void SetRaw(void* value) { raw_ = value; } 36 GetRawAtomic()37 const void* GetRawAtomic() const { 38 return reinterpret_cast<const std::atomic<const void*>*>(&raw_)->load( 39 std::memory_order_relaxed); 40 } SetRawAtomic(const void * value)41 void SetRawAtomic(const void* value) { 42 reinterpret_cast<std::atomic<const void*>*>(&raw_)->store( 43 value, std::memory_order_relaxed); 44 } 45 ClearFromGC()46 void ClearFromGC() const { raw_ = nullptr; } 47 48 private: 49 // All constructors initialize `raw_`. Do not add a default value here as it 50 // results in a non-atomic write on some builds, even when the atomic version 51 // of the constructor is used. 52 mutable const void* raw_; 53 }; 54 55 // The basic class from which all Member classes are 'generated'. 56 template <typename T, typename WeaknessTag, typename WriteBarrierPolicy, 57 typename CheckingPolicy> 58 class BasicMember final : private MemberBase, private CheckingPolicy { 59 public: 60 using PointeeType = T; 61 62 constexpr BasicMember() = default; BasicMember(std::nullptr_t)63 constexpr BasicMember(std::nullptr_t) {} // NOLINT BasicMember(SentinelPointer s)64 BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT BasicMember(T * raw)65 BasicMember(T* raw) : MemberBase(raw) { // NOLINT 66 InitializingWriteBarrier(); 67 this->CheckPointer(Get()); 68 } BasicMember(T & raw)69 BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT 70 // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to 71 // initialize using atomic assignments. This is required for preventing 72 // data races with concurrent marking. 73 using AtomicInitializerTag = MemberBase::AtomicInitializerTag; BasicMember(std::nullptr_t,AtomicInitializerTag atomic)74 BasicMember(std::nullptr_t, AtomicInitializerTag atomic) 75 : MemberBase(nullptr, atomic) {} BasicMember(SentinelPointer s,AtomicInitializerTag atomic)76 BasicMember(SentinelPointer s, AtomicInitializerTag atomic) 77 : MemberBase(s, atomic) {} BasicMember(T * raw,AtomicInitializerTag atomic)78 BasicMember(T* raw, AtomicInitializerTag atomic) : MemberBase(raw, atomic) { 79 InitializingWriteBarrier(); 80 this->CheckPointer(Get()); 81 } BasicMember(T & raw,AtomicInitializerTag atomic)82 BasicMember(T& raw, AtomicInitializerTag atomic) 83 : BasicMember(&raw, atomic) {} 84 // Copy ctor. BasicMember(const BasicMember & other)85 BasicMember(const BasicMember& other) : BasicMember(other.Get()) {} 86 // Allow heterogeneous construction. 87 template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag, 88 typename OtherCheckingPolicy, 89 typename = std::enable_if_t<std::is_base_of<T, U>::value>> BasicMember(const BasicMember<U,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> & other)90 BasicMember( // NOLINT 91 const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, 92 OtherCheckingPolicy>& other) 93 : BasicMember(other.Get()) {} 94 // Move ctor. BasicMember(BasicMember && other)95 BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) { 96 other.Clear(); 97 } 98 // Allow heterogeneous move construction. 99 template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag, 100 typename OtherCheckingPolicy, 101 typename = std::enable_if_t<std::is_base_of<T, U>::value>> BasicMember(BasicMember<U,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> && other)102 BasicMember(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, 103 OtherCheckingPolicy>&& other) noexcept 104 : BasicMember(other.Get()) { 105 other.Clear(); 106 } 107 // Construction from Persistent. 108 template <typename U, typename PersistentWeaknessPolicy, 109 typename PersistentLocationPolicy, 110 typename PersistentCheckingPolicy, 111 typename = std::enable_if_t<std::is_base_of<T, U>::value>> BasicMember(const BasicPersistent<U,PersistentWeaknessPolicy,PersistentLocationPolicy,PersistentCheckingPolicy> & p)112 BasicMember(const BasicPersistent<U, PersistentWeaknessPolicy, 113 PersistentLocationPolicy, 114 PersistentCheckingPolicy>& p) 115 : BasicMember(p.Get()) {} 116 117 // Copy assignment. 118 BasicMember& operator=(const BasicMember& other) { 119 return operator=(other.Get()); 120 } 121 // Allow heterogeneous copy assignment. 122 template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy, 123 typename OtherCheckingPolicy, 124 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 125 BasicMember& operator=( 126 const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, 127 OtherCheckingPolicy>& other) { 128 return operator=(other.Get()); 129 } 130 // Move assignment. 131 BasicMember& operator=(BasicMember&& other) noexcept { 132 operator=(other.Get()); 133 other.Clear(); 134 return *this; 135 } 136 // Heterogeneous move assignment. 137 template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy, 138 typename OtherCheckingPolicy, 139 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 140 BasicMember& operator=(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, 141 OtherCheckingPolicy>&& other) noexcept { 142 operator=(other.Get()); 143 other.Clear(); 144 return *this; 145 } 146 // Assignment from Persistent. 147 template <typename U, typename PersistentWeaknessPolicy, 148 typename PersistentLocationPolicy, 149 typename PersistentCheckingPolicy, 150 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 151 BasicMember& operator=( 152 const BasicPersistent<U, PersistentWeaknessPolicy, 153 PersistentLocationPolicy, PersistentCheckingPolicy>& 154 other) { 155 return operator=(other.Get()); 156 } 157 BasicMember& operator=(T* other) { 158 SetRawAtomic(other); 159 AssigningWriteBarrier(); 160 this->CheckPointer(Get()); 161 return *this; 162 } 163 BasicMember& operator=(std::nullptr_t) { 164 Clear(); 165 return *this; 166 } 167 BasicMember& operator=(SentinelPointer s) { 168 SetRawAtomic(s); 169 return *this; 170 } 171 172 template <typename OtherWeaknessTag, typename OtherBarrierPolicy, 173 typename OtherCheckingPolicy> Swap(BasicMember<T,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> & other)174 void Swap(BasicMember<T, OtherWeaknessTag, OtherBarrierPolicy, 175 OtherCheckingPolicy>& other) { 176 T* tmp = Get(); 177 *this = other; 178 other = tmp; 179 } 180 181 explicit operator bool() const { return Get(); } 182 operator T*() const { return Get(); } 183 T* operator->() const { return Get(); } 184 T& operator*() const { return *Get(); } 185 186 // CFI cast exemption to allow passing SentinelPointer through T* and support 187 // heterogeneous assignments between different Member and Persistent handles 188 // based on their actual types. Get()189 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { 190 // Executed by the mutator, hence non atomic load. 191 // 192 // The const_cast below removes the constness from MemberBase storage. The 193 // following static_cast re-adds any constness if specified through the 194 // user-visible template parameter T. 195 return static_cast<T*>(const_cast<void*>(MemberBase::GetRaw())); 196 } 197 Clear()198 void Clear() { SetRawAtomic(nullptr); } 199 Release()200 T* Release() { 201 T* result = Get(); 202 Clear(); 203 return result; 204 } 205 GetSlotForTesting()206 const T** GetSlotForTesting() const { 207 return reinterpret_cast<const T**>(GetRawSlot()); 208 } 209 210 private: GetRawAtomic()211 const T* GetRawAtomic() const { 212 return static_cast<const T*>(MemberBase::GetRawAtomic()); 213 } 214 InitializingWriteBarrier()215 void InitializingWriteBarrier() const { 216 WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw()); 217 } AssigningWriteBarrier()218 void AssigningWriteBarrier() const { 219 WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw()); 220 } 221 ClearFromGC()222 void ClearFromGC() const { MemberBase::ClearFromGC(); } 223 GetFromGC()224 T* GetFromGC() const { return Get(); } 225 226 friend class cppgc::Visitor; 227 template <typename U> 228 friend struct cppgc::TraceTrait; 229 }; 230 231 template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1, 232 typename CheckingPolicy1, typename T2, typename WeaknessTag2, 233 typename WriteBarrierPolicy2, typename CheckingPolicy2> 234 bool operator==(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, 235 CheckingPolicy1>& member1, 236 const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, 237 CheckingPolicy2>& member2) { 238 return member1.Get() == member2.Get(); 239 } 240 241 template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1, 242 typename CheckingPolicy1, typename T2, typename WeaknessTag2, 243 typename WriteBarrierPolicy2, typename CheckingPolicy2> 244 bool operator!=(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, 245 CheckingPolicy1>& member1, 246 const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, 247 CheckingPolicy2>& member2) { 248 return !(member1 == member2); 249 } 250 251 template <typename T, typename WriteBarrierPolicy, typename CheckingPolicy> 252 struct IsWeak< 253 internal::BasicMember<T, WeakMemberTag, WriteBarrierPolicy, CheckingPolicy>> 254 : std::true_type {}; 255 256 } // namespace internal 257 258 /** 259 * Members are used in classes to contain strong pointers to other garbage 260 * collected objects. All Member fields of a class must be traced in the class' 261 * trace method. 262 */ 263 template <typename T> 264 using Member = internal::BasicMember<T, internal::StrongMemberTag, 265 internal::DijkstraWriteBarrierPolicy>; 266 267 /** 268 * WeakMember is similar to Member in that it is used to point to other garbage 269 * collected objects. However instead of creating a strong pointer to the 270 * object, the WeakMember creates a weak pointer, which does not keep the 271 * pointee alive. Hence if all pointers to to a heap allocated object are weak 272 * the object will be garbage collected. At the time of GC the weak pointers 273 * will automatically be set to null. 274 */ 275 template <typename T> 276 using WeakMember = internal::BasicMember<T, internal::WeakMemberTag, 277 internal::DijkstraWriteBarrierPolicy>; 278 279 /** 280 * UntracedMember is a pointer to an on-heap object that is not traced for some 281 * reason. Do not use this unless you know what you are doing. Keeping raw 282 * pointers to on-heap objects is prohibited unless used from stack. Pointee 283 * must be kept alive through other means. 284 */ 285 template <typename T> 286 using UntracedMember = internal::BasicMember<T, internal::UntracedMemberTag, 287 internal::NoWriteBarrierPolicy>; 288 289 } // namespace cppgc 290 291 #endif // INCLUDE_CPPGC_MEMBER_H_ 292