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 17namespace cppgc { 18 19class Visitor; 20 21namespace 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. 25class MemberBase { 26 protected: 27 struct AtomicInitializerTag {}; 28 29 MemberBase() : raw_(nullptr) {} 30 explicit MemberBase(const void* value) : raw_(value) {} 31 MemberBase(const void* value, AtomicInitializerTag) { SetRawAtomic(value); } 32 33 const void** GetRawSlot() const { return &raw_; } 34 const void* GetRaw() const { return raw_; } 35 void SetRaw(void* value) { raw_ = value; } 36 37 const void* GetRawAtomic() const { 38 return reinterpret_cast<const std::atomic<const void*>*>(&raw_)->load( 39 std::memory_order_relaxed); 40 } 41 void SetRawAtomic(const void* value) { 42 reinterpret_cast<std::atomic<const void*>*>(&raw_)->store( 43 value, std::memory_order_relaxed); 44 } 45 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'. 56template <typename T, typename WeaknessTag, typename WriteBarrierPolicy, 57 typename CheckingPolicy> 58class BasicMember final : private MemberBase, private CheckingPolicy { 59 public: 60 using PointeeType = T; 61 62 constexpr BasicMember() = default; 63 constexpr BasicMember(std::nullptr_t) {} // NOLINT 64 BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT 65 BasicMember(T* raw) : MemberBase(raw) { // NOLINT 66 InitializingWriteBarrier(); 67 this->CheckPointer(Get()); 68 } 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; 74 BasicMember(std::nullptr_t, AtomicInitializerTag atomic) 75 : MemberBase(nullptr, atomic) {} 76 BasicMember(SentinelPointer s, AtomicInitializerTag atomic) 77 : MemberBase(s, atomic) {} 78 BasicMember(T* raw, AtomicInitializerTag atomic) : MemberBase(raw, atomic) { 79 InitializingWriteBarrier(); 80 this->CheckPointer(Get()); 81 } 82 BasicMember(T& raw, AtomicInitializerTag atomic) 83 : BasicMember(&raw, atomic) {} 84 // Copy ctor. 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>> 90 BasicMember( // NOLINT 91 const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, 92 OtherCheckingPolicy>& other) 93 : BasicMember(other.Get()) {} 94 // Move ctor. 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>> 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>> 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> 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. 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 198 void Clear() { SetRawAtomic(nullptr); } 199 200 T* Release() { 201 T* result = Get(); 202 Clear(); 203 return result; 204 } 205 206 const T** GetSlotForTesting() const { 207 return reinterpret_cast<const T**>(GetRawSlot()); 208 } 209 210 private: 211 const T* GetRawAtomic() const { 212 return static_cast<const T*>(MemberBase::GetRawAtomic()); 213 } 214 215 void InitializingWriteBarrier() const { 216 WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw()); 217 } 218 void AssigningWriteBarrier() const { 219 WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw()); 220 } 221 222 void ClearFromGC() const { MemberBase::ClearFromGC(); } 223 224 T* GetFromGC() const { return Get(); } 225 226 friend class cppgc::Visitor; 227 template <typename U> 228 friend struct cppgc::TraceTrait; 229}; 230 231template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1, 232 typename CheckingPolicy1, typename T2, typename WeaknessTag2, 233 typename WriteBarrierPolicy2, typename CheckingPolicy2> 234bool 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 241template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1, 242 typename CheckingPolicy1, typename T2, typename WeaknessTag2, 243 typename WriteBarrierPolicy2, typename CheckingPolicy2> 244bool operator!=(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, 245 CheckingPolicy1>& member1, 246 const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, 247 CheckingPolicy2>& member2) { 248 return !(member1 == member2); 249} 250 251template <typename T, typename WriteBarrierPolicy, typename CheckingPolicy> 252struct 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 */ 263template <typename T> 264using 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 */ 275template <typename T> 276using 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 */ 285template <typename T> 286using UntracedMember = internal::BasicMember<T, internal::UntracedMemberTag, 287 internal::NoWriteBarrierPolicy>; 288 289} // namespace cppgc 290 291#endif // INCLUDE_CPPGC_MEMBER_H_ 292