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_CROSS_THREAD_PERSISTENT_H_ 6#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ 7 8#include <atomic> 9 10#include "cppgc/internal/persistent-node.h" 11#include "cppgc/internal/pointer-policies.h" 12#include "cppgc/persistent.h" 13#include "cppgc/visitor.h" 14 15namespace cppgc { 16namespace internal { 17 18// Wrapper around PersistentBase that allows accessing poisoned memory when 19// using ASAN. This is needed as the GC of the heap that owns the value 20// of a CTP, may clear it (heap termination, weakness) while the object 21// holding the CTP may be poisoned as itself may be deemed dead. 22class CrossThreadPersistentBase : public PersistentBase { 23 public: 24 CrossThreadPersistentBase() = default; 25 explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {} 26 27 V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const { 28 return raw_; 29 } 30 31 V8_CLANG_NO_SANITIZE("address") 32 PersistentNode* GetNodeFromGC() const { return node_; } 33 34 V8_CLANG_NO_SANITIZE("address") 35 void ClearFromGC() const { 36 raw_ = nullptr; 37 SetNodeSafe(nullptr); 38 } 39 40 // GetNodeSafe() can be used for a thread-safe IsValid() check in a 41 // double-checked locking pattern. See ~BasicCrossThreadPersistent. 42 PersistentNode* GetNodeSafe() const { 43 return reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->load( 44 std::memory_order_acquire); 45 } 46 47 // The GC writes using SetNodeSafe() while holding the lock. 48 V8_CLANG_NO_SANITIZE("address") 49 void SetNodeSafe(PersistentNode* value) const { 50#if defined(__has_feature) 51#if __has_feature(address_sanitizer) 52#define V8_IS_ASAN 1 53#endif 54#endif 55 56#ifdef V8_IS_ASAN 57 __atomic_store(&node_, &value, __ATOMIC_RELEASE); 58#else // !V8_IS_ASAN 59 // Non-ASAN builds can use atomics. This also covers MSVC which does not 60 // have the __atomic_store intrinsic. 61 reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->store( 62 value, std::memory_order_release); 63#endif // !V8_IS_ASAN 64 65#undef V8_IS_ASAN 66 } 67}; 68 69template <typename T, typename WeaknessPolicy, typename LocationPolicy, 70 typename CheckingPolicy> 71class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, 72 public LocationPolicy, 73 private WeaknessPolicy, 74 private CheckingPolicy { 75 public: 76 using typename WeaknessPolicy::IsStrongPersistent; 77 using PointeeType = T; 78 79 ~BasicCrossThreadPersistent() { 80 // This implements fast path for destroying empty/sentinel. 81 // 82 // Simplified version of `AssignUnsafe()` to allow calling without a 83 // complete type `T`. Uses double-checked locking with a simple thread-safe 84 // check for a valid handle based on a node. 85 if (GetNodeSafe()) { 86 PersistentRegionLock guard; 87 const void* old_value = GetValue(); 88 // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck 89 // validity while holding the lock to ensure the reference has not been 90 // cleared. 91 if (IsValid(old_value)) { 92 CrossThreadPersistentRegion& region = 93 this->GetPersistentRegion(old_value); 94 region.FreeNode(GetNode()); 95 SetNode(nullptr); 96 } else { 97 CPPGC_DCHECK(!GetNode()); 98 } 99 } 100 // No need to call SetValue() as the handle is not used anymore. This can 101 // leave behind stale sentinel values but will always destroy the underlying 102 // node. 103 } 104 105 BasicCrossThreadPersistent( 106 const SourceLocation& loc = SourceLocation::Current()) 107 : LocationPolicy(loc) {} 108 109 BasicCrossThreadPersistent( 110 std::nullptr_t, const SourceLocation& loc = SourceLocation::Current()) 111 : LocationPolicy(loc) {} 112 113 BasicCrossThreadPersistent( 114 SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) 115 : CrossThreadPersistentBase(s), LocationPolicy(loc) {} 116 117 BasicCrossThreadPersistent( 118 T* raw, const SourceLocation& loc = SourceLocation::Current()) 119 : CrossThreadPersistentBase(raw), LocationPolicy(loc) { 120 if (!IsValid(raw)) return; 121 PersistentRegionLock guard; 122 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); 123 SetNode(region.AllocateNode(this, &TraceAsRoot)); 124 this->CheckPointer(raw); 125 } 126 127 class UnsafeCtorTag { 128 private: 129 UnsafeCtorTag() = default; 130 template <typename U, typename OtherWeaknessPolicy, 131 typename OtherLocationPolicy, typename OtherCheckingPolicy> 132 friend class BasicCrossThreadPersistent; 133 }; 134 135 BasicCrossThreadPersistent( 136 UnsafeCtorTag, T* raw, 137 const SourceLocation& loc = SourceLocation::Current()) 138 : CrossThreadPersistentBase(raw), LocationPolicy(loc) { 139 if (!IsValid(raw)) return; 140 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); 141 SetNode(region.AllocateNode(this, &TraceAsRoot)); 142 this->CheckPointer(raw); 143 } 144 145 BasicCrossThreadPersistent( 146 T& raw, const SourceLocation& loc = SourceLocation::Current()) 147 : BasicCrossThreadPersistent(&raw, loc) {} 148 149 template <typename U, typename MemberBarrierPolicy, 150 typename MemberWeaknessTag, typename MemberCheckingPolicy, 151 typename MemberStorageType, 152 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 153 BasicCrossThreadPersistent( 154 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, 155 MemberCheckingPolicy, MemberStorageType> 156 member, 157 const SourceLocation& loc = SourceLocation::Current()) 158 : BasicCrossThreadPersistent(member.Get(), loc) {} 159 160 BasicCrossThreadPersistent( 161 const BasicCrossThreadPersistent& other, 162 const SourceLocation& loc = SourceLocation::Current()) 163 : BasicCrossThreadPersistent(loc) { 164 // Invoke operator=. 165 *this = other; 166 } 167 168 // Heterogeneous ctor. 169 template <typename U, typename OtherWeaknessPolicy, 170 typename OtherLocationPolicy, typename OtherCheckingPolicy, 171 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 172 BasicCrossThreadPersistent( 173 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy, 174 OtherLocationPolicy, 175 OtherCheckingPolicy>& other, 176 const SourceLocation& loc = SourceLocation::Current()) 177 : BasicCrossThreadPersistent(loc) { 178 *this = other; 179 } 180 181 BasicCrossThreadPersistent( 182 BasicCrossThreadPersistent&& other, 183 const SourceLocation& loc = SourceLocation::Current()) noexcept { 184 // Invoke operator=. 185 *this = std::move(other); 186 } 187 188 BasicCrossThreadPersistent& operator=( 189 const BasicCrossThreadPersistent& other) { 190 PersistentRegionLock guard; 191 AssignSafe(guard, other.Get()); 192 return *this; 193 } 194 195 template <typename U, typename OtherWeaknessPolicy, 196 typename OtherLocationPolicy, typename OtherCheckingPolicy, 197 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 198 BasicCrossThreadPersistent& operator=( 199 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy, 200 OtherLocationPolicy, 201 OtherCheckingPolicy>& other) { 202 PersistentRegionLock guard; 203 AssignSafe(guard, other.Get()); 204 return *this; 205 } 206 207 BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) { 208 if (this == &other) return *this; 209 Clear(); 210 PersistentRegionLock guard; 211 PersistentBase::operator=(std::move(other)); 212 LocationPolicy::operator=(std::move(other)); 213 if (!IsValid(GetValue())) return *this; 214 GetNode()->UpdateOwner(this); 215 other.SetValue(nullptr); 216 other.SetNode(nullptr); 217 this->CheckPointer(Get()); 218 return *this; 219 } 220 221 /** 222 * Assigns a raw pointer. 223 * 224 * Note: **Not thread-safe.** 225 */ 226 BasicCrossThreadPersistent& operator=(T* other) { 227 AssignUnsafe(other); 228 return *this; 229 } 230 231 // Assignment from member. 232 template <typename U, typename MemberBarrierPolicy, 233 typename MemberWeaknessTag, typename MemberCheckingPolicy, 234 typename MemberStorageType, 235 typename = std::enable_if_t<std::is_base_of<T, U>::value>> 236 BasicCrossThreadPersistent& operator=( 237 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, 238 MemberCheckingPolicy, MemberStorageType> 239 member) { 240 return operator=(member.Get()); 241 } 242 243 /** 244 * Assigns a nullptr. 245 * 246 * \returns the handle. 247 */ 248 BasicCrossThreadPersistent& operator=(std::nullptr_t) { 249 Clear(); 250 return *this; 251 } 252 253 /** 254 * Assigns the sentinel pointer. 255 * 256 * \returns the handle. 257 */ 258 BasicCrossThreadPersistent& operator=(SentinelPointer s) { 259 PersistentRegionLock guard; 260 AssignSafe(guard, s); 261 return *this; 262 } 263 264 /** 265 * Returns a pointer to the stored object. 266 * 267 * Note: **Not thread-safe.** 268 * 269 * \returns a pointer to the stored object. 270 */ 271 // CFI cast exemption to allow passing SentinelPointer through T* and support 272 // heterogeneous assignments between different Member and Persistent handles 273 // based on their actual types. 274 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { 275 return static_cast<T*>(const_cast<void*>(GetValue())); 276 } 277 278 /** 279 * Clears the stored object. 280 */ 281 void Clear() { 282 PersistentRegionLock guard; 283 AssignSafe(guard, nullptr); 284 } 285 286 /** 287 * Returns a pointer to the stored object and releases it. 288 * 289 * Note: **Not thread-safe.** 290 * 291 * \returns a pointer to the stored object. 292 */ 293 T* Release() { 294 T* result = Get(); 295 Clear(); 296 return result; 297 } 298 299 /** 300 * Conversio to boolean. 301 * 302 * Note: **Not thread-safe.** 303 * 304 * \returns true if an actual object has been stored and false otherwise. 305 */ 306 explicit operator bool() const { return Get(); } 307 308 /** 309 * Conversion to object of type T. 310 * 311 * Note: **Not thread-safe.** 312 * 313 * \returns the object. 314 */ 315 operator T*() const { return Get(); } 316 317 /** 318 * Dereferences the stored object. 319 * 320 * Note: **Not thread-safe.** 321 */ 322 T* operator->() const { return Get(); } 323 T& operator*() const { return *Get(); } 324 325 template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy, 326 typename OtherLocationPolicy = LocationPolicy, 327 typename OtherCheckingPolicy = CheckingPolicy> 328 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, 329 OtherCheckingPolicy> 330 To() const { 331 using OtherBasicCrossThreadPersistent = 332 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, 333 OtherCheckingPolicy>; 334 PersistentRegionLock guard; 335 return OtherBasicCrossThreadPersistent( 336 typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(), 337 static_cast<U*>(Get())); 338 } 339 340 template <typename U = T, 341 typename = typename std::enable_if<!BasicCrossThreadPersistent< 342 U, WeaknessPolicy>::IsStrongPersistent::value>::type> 343 BasicCrossThreadPersistent<U, internal::StrongCrossThreadPersistentPolicy> 344 Lock() const { 345 return BasicCrossThreadPersistent< 346 U, internal::StrongCrossThreadPersistentPolicy>(*this); 347 } 348 349 private: 350 static bool IsValid(const void* ptr) { 351 return ptr && ptr != kSentinelPointer; 352 } 353 354 static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { 355 root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr)); 356 } 357 358 void AssignUnsafe(T* ptr) { 359 const void* old_value = GetValue(); 360 if (IsValid(old_value)) { 361 PersistentRegionLock guard; 362 old_value = GetValue(); 363 // The fast path check (IsValid()) does not acquire the lock. Reload 364 // the value to ensure the reference has not been cleared. 365 if (IsValid(old_value)) { 366 CrossThreadPersistentRegion& region = 367 this->GetPersistentRegion(old_value); 368 if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { 369 SetValue(ptr); 370 this->CheckPointer(ptr); 371 return; 372 } 373 region.FreeNode(GetNode()); 374 SetNode(nullptr); 375 } else { 376 CPPGC_DCHECK(!GetNode()); 377 } 378 } 379 SetValue(ptr); 380 if (!IsValid(ptr)) return; 381 PersistentRegionLock guard; 382 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); 383 this->CheckPointer(ptr); 384 } 385 386 void AssignSafe(PersistentRegionLock&, T* ptr) { 387 PersistentRegionLock::AssertLocked(); 388 const void* old_value = GetValue(); 389 if (IsValid(old_value)) { 390 CrossThreadPersistentRegion& region = 391 this->GetPersistentRegion(old_value); 392 if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { 393 SetValue(ptr); 394 this->CheckPointer(ptr); 395 return; 396 } 397 region.FreeNode(GetNode()); 398 SetNode(nullptr); 399 } 400 SetValue(ptr); 401 if (!IsValid(ptr)) return; 402 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); 403 this->CheckPointer(ptr); 404 } 405 406 void ClearFromGC() const { 407 if (IsValid(GetValueFromGC())) { 408 WeaknessPolicy::GetPersistentRegion(GetValueFromGC()) 409 .FreeNode(GetNodeFromGC()); 410 CrossThreadPersistentBase::ClearFromGC(); 411 } 412 } 413 414 // See Get() for details. 415 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") 416 T* GetFromGC() const { 417 return static_cast<T*>(const_cast<void*>(GetValueFromGC())); 418 } 419 420 friend class internal::RootVisitor; 421}; 422 423template <typename T, typename LocationPolicy, typename CheckingPolicy> 424struct IsWeak< 425 BasicCrossThreadPersistent<T, internal::WeakCrossThreadPersistentPolicy, 426 LocationPolicy, CheckingPolicy>> 427 : std::true_type {}; 428 429} // namespace internal 430 431namespace subtle { 432 433/** 434 * **DO NOT USE: Has known caveats, see below.** 435 * 436 * CrossThreadPersistent allows retaining objects from threads other than the 437 * thread the owning heap is operating on. 438 * 439 * Known caveats: 440 * - Does not protect the heap owning an object from terminating. 441 * - Reaching transitively through the graph is unsupported as objects may be 442 * moved concurrently on the thread owning the object. 443 */ 444template <typename T> 445using CrossThreadPersistent = internal::BasicCrossThreadPersistent< 446 T, internal::StrongCrossThreadPersistentPolicy>; 447 448/** 449 * **DO NOT USE: Has known caveats, see below.** 450 * 451 * CrossThreadPersistent allows weakly retaining objects from threads other than 452 * the thread the owning heap is operating on. 453 * 454 * Known caveats: 455 * - Does not protect the heap owning an object from terminating. 456 * - Reaching transitively through the graph is unsupported as objects may be 457 * moved concurrently on the thread owning the object. 458 */ 459template <typename T> 460using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent< 461 T, internal::WeakCrossThreadPersistentPolicy>; 462 463} // namespace subtle 464} // namespace cppgc 465 466#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ 467