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_PERSISTENT_H_ 6 #define INCLUDE_CPPGC_PERSISTENT_H_ 7 8 #include <type_traits> 9 10 #include "cppgc/internal/persistent-node.h" 11 #include "cppgc/internal/pointer-policies.h" 12 #include "cppgc/source-location.h" 13 #include "cppgc/type-traits.h" 14 #include "cppgc/visitor.h" 15 #include "v8config.h" // NOLINT(build/include_directory) 16 17 namespace cppgc { 18 19 class Visitor; 20 21 namespace internal { 22 23 class PersistentBase { 24 protected: 25 PersistentBase() = default; PersistentBase(void * raw)26 explicit PersistentBase(void* raw) : raw_(raw) {} 27 GetValue()28 void* GetValue() const { return raw_; } SetValue(void * value)29 void SetValue(void* value) { raw_ = value; } 30 GetNode()31 PersistentNode* GetNode() const { return node_; } SetNode(PersistentNode * node)32 void SetNode(PersistentNode* node) { node_ = node; } 33 34 // Performs a shallow clear which assumes that internal persistent nodes are 35 // destroyed elsewhere. ClearFromGC()36 void ClearFromGC() const { 37 raw_ = nullptr; 38 node_ = nullptr; 39 } 40 41 private: 42 mutable void* raw_ = nullptr; 43 mutable PersistentNode* node_ = nullptr; 44 45 friend class PersistentRegion; 46 }; 47 48 // The basic class from which all Persistent classes are generated. 49 template <typename T, typename WeaknessPolicy, typename LocationPolicy, 50 typename CheckingPolicy> 51 class BasicPersistent final : public PersistentBase, 52 public LocationPolicy, 53 private WeaknessPolicy, 54 private CheckingPolicy { 55 public: 56 using typename WeaknessPolicy::IsStrongPersistent; 57 using PointeeType = T; 58 59 // Null-state/sentinel constructors. 60 BasicPersistent( // NOLINT 61 const SourceLocation& loc = SourceLocation::Current()) LocationPolicy(loc)62 : LocationPolicy(loc) {} 63 64 BasicPersistent(std::nullptr_t, // NOLINT 65 const SourceLocation& loc = SourceLocation::Current()) LocationPolicy(loc)66 : LocationPolicy(loc) {} 67 68 BasicPersistent( // NOLINT 69 SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) PersistentBase(s)70 : PersistentBase(s), LocationPolicy(loc) {} 71 72 // Raw value constructors. 73 BasicPersistent(T* raw, // NOLINT 74 const SourceLocation& loc = SourceLocation::Current()) PersistentBase(raw)75 : PersistentBase(raw), LocationPolicy(loc) { 76 if (!IsValid()) return; 77 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) 78 .AllocateNode(this, &BasicPersistent::Trace)); 79 this->CheckPointer(Get()); 80 } 81 82 BasicPersistent(T& raw, // NOLINT 83 const SourceLocation& loc = SourceLocation::Current()) 84 : BasicPersistent(&raw, loc) {} 85 86 // Copy ctor. 87 BasicPersistent(const BasicPersistent& other, 88 const SourceLocation& loc = SourceLocation::Current()) 89 : BasicPersistent(other.Get(), loc) {} 90 91 // Heterogeneous ctor. 92 template <typename U, typename OtherWeaknessPolicy, 93 typename OtherLocationPolicy, typename OtherCheckingPolicy, 94 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 95 BasicPersistent( // NOLINT 96 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, 97 OtherCheckingPolicy>& other, 98 const SourceLocation& loc = SourceLocation::Current()) 99 : BasicPersistent(other.Get(), loc) {} 100 101 // Move ctor. The heterogeneous move ctor is not supported since e.g. 102 // persistent can't reuse persistent node from weak persistent. 103 BasicPersistent( 104 BasicPersistent&& other, 105 const SourceLocation& loc = SourceLocation::Current()) noexcept PersistentBase(std::move (other))106 : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) { 107 if (!IsValid()) return; 108 GetNode()->UpdateOwner(this); 109 other.SetValue(nullptr); 110 other.SetNode(nullptr); 111 this->CheckPointer(Get()); 112 } 113 114 // Constructor from member. 115 template <typename U, typename MemberBarrierPolicy, 116 typename MemberWeaknessTag, typename MemberCheckingPolicy, 117 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 118 BasicPersistent(internal::BasicMember<U, MemberBarrierPolicy, // NOLINT 119 MemberWeaknessTag, MemberCheckingPolicy> 120 member, 121 const SourceLocation& loc = SourceLocation::Current()) 122 : BasicPersistent(member.Get(), loc) {} 123 ~BasicPersistent()124 ~BasicPersistent() { Clear(); } 125 126 // Copy assignment. 127 BasicPersistent& operator=(const BasicPersistent& other) { 128 return operator=(other.Get()); 129 } 130 131 template <typename U, typename OtherWeaknessPolicy, 132 typename OtherLocationPolicy, typename OtherCheckingPolicy, 133 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 134 BasicPersistent& operator=( 135 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, 136 OtherCheckingPolicy>& other) { 137 return operator=(other.Get()); 138 } 139 140 // Move assignment. 141 BasicPersistent& operator=(BasicPersistent&& other) { 142 if (this == &other) return *this; 143 Clear(); 144 PersistentBase::operator=(std::move(other)); 145 LocationPolicy::operator=(std::move(other)); 146 if (!IsValid()) return *this; 147 GetNode()->UpdateOwner(this); 148 other.SetValue(nullptr); 149 other.SetNode(nullptr); 150 this->CheckPointer(Get()); 151 return *this; 152 } 153 154 // Assignment from member. 155 template <typename U, typename MemberBarrierPolicy, 156 typename MemberWeaknessTag, typename MemberCheckingPolicy, 157 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 158 BasicPersistent& operator=( 159 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, 160 MemberCheckingPolicy> 161 member) { 162 return operator=(member.Get()); 163 } 164 165 BasicPersistent& operator=(T* other) { 166 Assign(other); 167 return *this; 168 } 169 170 BasicPersistent& operator=(std::nullptr_t) { 171 Clear(); 172 return *this; 173 } 174 175 BasicPersistent& operator=(SentinelPointer s) { 176 Assign(s); 177 return *this; 178 } 179 180 explicit operator bool() const { return Get(); } 181 operator T*() const { return Get(); } 182 T* operator->() const { return Get(); } 183 T& operator*() const { return *Get(); } 184 185 // CFI cast exemption to allow passing SentinelPointer through T* and support 186 // heterogeneous assignments between different Member and Persistent handles 187 // based on their actual types. Get()188 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { 189 return static_cast<T*>(GetValue()); 190 } 191 Clear()192 void Clear() { Assign(nullptr); } 193 Release()194 T* Release() { 195 T* result = Get(); 196 Clear(); 197 return result; 198 } 199 200 private: Trace(Visitor * v,const void * ptr)201 static void Trace(Visitor* v, const void* ptr) { 202 const auto* persistent = static_cast<const BasicPersistent*>(ptr); 203 v->TraceRoot(*persistent, persistent->Location()); 204 } 205 IsValid()206 bool IsValid() const { 207 // Ideally, handling kSentinelPointer would be done by the embedder. On the 208 // other hand, having Persistent aware of it is beneficial since no node 209 // gets wasted. 210 return GetValue() != nullptr && GetValue() != kSentinelPointer; 211 } 212 Assign(T * ptr)213 void Assign(T* ptr) { 214 if (IsValid()) { 215 if (ptr && ptr != kSentinelPointer) { 216 // Simply assign the pointer reusing the existing node. 217 SetValue(ptr); 218 this->CheckPointer(ptr); 219 return; 220 } 221 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); 222 SetNode(nullptr); 223 } 224 SetValue(ptr); 225 if (!IsValid()) return; 226 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) 227 .AllocateNode(this, &BasicPersistent::Trace)); 228 this->CheckPointer(Get()); 229 } 230 ClearFromGC()231 void ClearFromGC() const { 232 if (IsValid()) { 233 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); 234 PersistentBase::ClearFromGC(); 235 } 236 } 237 238 friend class cppgc::Visitor; 239 }; 240 241 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1, 242 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2, 243 typename LocationPolicy2, typename CheckingPolicy2> 244 bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, 245 CheckingPolicy1>& p1, 246 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, 247 CheckingPolicy2>& p2) { 248 return p1.Get() == p2.Get(); 249 } 250 251 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1, 252 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2, 253 typename LocationPolicy2, typename CheckingPolicy2> 254 bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, 255 CheckingPolicy1>& p1, 256 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, 257 CheckingPolicy2>& p2) { 258 return !(p1 == p2); 259 } 260 261 template <typename T1, typename PersistentWeaknessPolicy, 262 typename PersistentLocationPolicy, typename PersistentCheckingPolicy, 263 typename T2, typename MemberWriteBarrierPolicy, 264 typename MemberWeaknessTag, typename MemberCheckingPolicy> 265 bool operator==(const BasicPersistent<T1, PersistentWeaknessPolicy, 266 PersistentLocationPolicy, 267 PersistentCheckingPolicy>& p, 268 BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, 269 MemberCheckingPolicy> 270 m) { 271 return p.Get() == m.Get(); 272 } 273 274 template <typename T1, typename PersistentWeaknessPolicy, 275 typename PersistentLocationPolicy, typename PersistentCheckingPolicy, 276 typename T2, typename MemberWriteBarrierPolicy, 277 typename MemberWeaknessTag, typename MemberCheckingPolicy> 278 bool operator!=(const BasicPersistent<T1, PersistentWeaknessPolicy, 279 PersistentLocationPolicy, 280 PersistentCheckingPolicy>& p, 281 BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, 282 MemberCheckingPolicy> 283 m) { 284 return !(p == m); 285 } 286 287 template <typename T1, typename MemberWriteBarrierPolicy, 288 typename MemberWeaknessTag, typename MemberCheckingPolicy, 289 typename T2, typename PersistentWeaknessPolicy, 290 typename PersistentLocationPolicy, typename PersistentCheckingPolicy> 291 bool operator==(BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, 292 MemberCheckingPolicy> 293 m, 294 const BasicPersistent<T1, PersistentWeaknessPolicy, 295 PersistentLocationPolicy, 296 PersistentCheckingPolicy>& p) { 297 return m.Get() == p.Get(); 298 } 299 300 template <typename T1, typename MemberWriteBarrierPolicy, 301 typename MemberWeaknessTag, typename MemberCheckingPolicy, 302 typename T2, typename PersistentWeaknessPolicy, 303 typename PersistentLocationPolicy, typename PersistentCheckingPolicy> 304 bool operator!=(BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, 305 MemberCheckingPolicy> 306 m, 307 const BasicPersistent<T1, PersistentWeaknessPolicy, 308 PersistentLocationPolicy, 309 PersistentCheckingPolicy>& p) { 310 return !(m == p); 311 } 312 313 template <typename T, typename LocationPolicy, typename CheckingPolicy> 314 struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy, 315 CheckingPolicy>> : std::true_type {}; 316 } // namespace internal 317 318 /** 319 * Persistent is a way to create a strong pointer from an off-heap object to 320 * another on-heap object. As long as the Persistent handle is alive the GC will 321 * keep the object pointed to alive. The Persistent handle is always a GC root 322 * from the point of view of the GC. Persistent must be constructed and 323 * destructed in the same thread. 324 */ 325 template <typename T> 326 using Persistent = 327 internal::BasicPersistent<T, internal::StrongPersistentPolicy>; 328 329 /** 330 * WeakPersistent is a way to create a weak pointer from an off-heap object to 331 * an on-heap object. The pointer is automatically cleared when the pointee gets 332 * collected. WeakPersistent must be constructed and destructed in the same 333 * thread. 334 */ 335 template <typename T> 336 using WeakPersistent = 337 internal::BasicPersistent<T, internal::WeakPersistentPolicy>; 338 339 } // namespace cppgc 340 341 #endif // INCLUDE_CPPGC_PERSISTENT_H_ 342