Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-26 09:14:40

0001 // Copyright 2020 the V8 project authors. All rights reserved.
0002 // Use of this source code is governed by a BSD-style license that can be
0003 // found in the LICENSE file.
0004 
0005 #ifndef INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
0006 #define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
0007 
0008 #include <atomic>
0009 
0010 #include "cppgc/internal/persistent-node.h"
0011 #include "cppgc/internal/pointer-policies.h"
0012 #include "cppgc/persistent.h"
0013 #include "cppgc/visitor.h"
0014 
0015 namespace cppgc {
0016 namespace internal {
0017 
0018 // Wrapper around PersistentBase that allows accessing poisoned memory when
0019 // using ASAN. This is needed as the GC of the heap that owns the value
0020 // of a CTP, may clear it (heap termination, weakness) while the object
0021 // holding the CTP may be poisoned as itself may be deemed dead.
0022 class CrossThreadPersistentBase : public PersistentBase {
0023  public:
0024   CrossThreadPersistentBase() = default;
0025   explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {}
0026 
0027   V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const {
0028     return raw_;
0029   }
0030 
0031   V8_CLANG_NO_SANITIZE("address")
0032   PersistentNode* GetNodeFromGC() const { return node_; }
0033 
0034   V8_CLANG_NO_SANITIZE("address")
0035   void ClearFromGC() const {
0036     raw_ = nullptr;
0037     SetNodeSafe(nullptr);
0038   }
0039 
0040   // GetNodeSafe() can be used for a thread-safe IsValid() check in a
0041   // double-checked locking pattern. See ~BasicCrossThreadPersistent.
0042   PersistentNode* GetNodeSafe() const {
0043     return reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->load(
0044         std::memory_order_acquire);
0045   }
0046 
0047   // The GC writes using SetNodeSafe() while holding the lock.
0048   V8_CLANG_NO_SANITIZE("address")
0049   void SetNodeSafe(PersistentNode* value) const {
0050 #if defined(__has_feature)
0051 #if __has_feature(address_sanitizer)
0052 #define V8_IS_ASAN 1
0053 #endif
0054 #endif
0055 
0056 #ifdef V8_IS_ASAN
0057     __atomic_store(&node_, &value, __ATOMIC_RELEASE);
0058 #else   // !V8_IS_ASAN
0059     // Non-ASAN builds can use atomics. This also covers MSVC which does not
0060     // have the __atomic_store intrinsic.
0061     reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->store(
0062         value, std::memory_order_release);
0063 #endif  // !V8_IS_ASAN
0064 
0065 #undef V8_IS_ASAN
0066   }
0067 };
0068 
0069 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
0070           typename CheckingPolicy>
0071 class BasicCrossThreadPersistent final : public CrossThreadPersistentBase,
0072                                          public LocationPolicy,
0073                                          private WeaknessPolicy,
0074                                          private CheckingPolicy {
0075  public:
0076   using typename WeaknessPolicy::IsStrongPersistent;
0077   using PointeeType = T;
0078 
0079   ~BasicCrossThreadPersistent() {
0080     //  This implements fast path for destroying empty/sentinel.
0081     //
0082     // Simplified version of `AssignUnsafe()` to allow calling without a
0083     // complete type `T`. Uses double-checked locking with a simple thread-safe
0084     // check for a valid handle based on a node.
0085     if (GetNodeSafe()) {
0086       PersistentRegionLock guard;
0087       const void* old_value = GetValue();
0088       // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck
0089       // validity while holding the lock to ensure the reference has not been
0090       // cleared.
0091       if (IsValid(old_value)) {
0092         CrossThreadPersistentRegion& region =
0093             this->GetPersistentRegion(old_value);
0094         region.FreeNode(GetNode());
0095         SetNode(nullptr);
0096       } else {
0097         CPPGC_DCHECK(!GetNode());
0098       }
0099     }
0100     // No need to call SetValue() as the handle is not used anymore. This can
0101     // leave behind stale sentinel values but will always destroy the underlying
0102     // node.
0103   }
0104 
0105   BasicCrossThreadPersistent(SourceLocation loc = SourceLocation::Current())
0106       : LocationPolicy(loc) {}
0107 
0108   BasicCrossThreadPersistent(std::nullptr_t,
0109                              SourceLocation loc = SourceLocation::Current())
0110       : LocationPolicy(loc) {}
0111 
0112   BasicCrossThreadPersistent(SentinelPointer s,
0113                              SourceLocation loc = SourceLocation::Current())
0114       : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
0115 
0116   BasicCrossThreadPersistent(T* raw,
0117                              SourceLocation loc = SourceLocation::Current())
0118       : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
0119     if (!IsValid(raw)) return;
0120     PersistentRegionLock guard;
0121     CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
0122     SetNode(region.AllocateNode(this, &TraceAsRoot));
0123     this->CheckPointer(raw);
0124   }
0125 
0126   class UnsafeCtorTag {
0127    private:
0128     UnsafeCtorTag() = default;
0129     template <typename U, typename OtherWeaknessPolicy,
0130               typename OtherLocationPolicy, typename OtherCheckingPolicy>
0131     friend class BasicCrossThreadPersistent;
0132   };
0133 
0134   BasicCrossThreadPersistent(UnsafeCtorTag, T* raw,
0135                              SourceLocation loc = SourceLocation::Current())
0136       : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
0137     if (!IsValid(raw)) return;
0138     CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
0139     SetNode(region.AllocateNode(this, &TraceAsRoot));
0140     this->CheckPointer(raw);
0141   }
0142 
0143   BasicCrossThreadPersistent(T& raw,
0144                              SourceLocation loc = SourceLocation::Current())
0145       : BasicCrossThreadPersistent(&raw, loc) {}
0146 
0147   template <typename U, typename MemberBarrierPolicy,
0148             typename MemberWeaknessTag, typename MemberCheckingPolicy,
0149             typename MemberStorageType,
0150             typename = std::enable_if_t<std::is_base_of_v<T, U>>>
0151   BasicCrossThreadPersistent(
0152       internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
0153                             MemberCheckingPolicy, MemberStorageType>
0154           member,
0155       SourceLocation loc = SourceLocation::Current())
0156       : BasicCrossThreadPersistent(member.Get(), loc) {}
0157 
0158   BasicCrossThreadPersistent(const BasicCrossThreadPersistent& other,
0159                              SourceLocation loc = SourceLocation::Current())
0160       : BasicCrossThreadPersistent(loc) {
0161     // Invoke operator=.
0162     *this = other;
0163   }
0164 
0165   // Heterogeneous ctor.
0166   template <typename U, typename OtherWeaknessPolicy,
0167             typename OtherLocationPolicy, typename OtherCheckingPolicy,
0168             typename = std::enable_if_t<std::is_base_of_v<T, U>>>
0169   BasicCrossThreadPersistent(const BasicCrossThreadPersistent<
0170                                  U, OtherWeaknessPolicy, OtherLocationPolicy,
0171                                  OtherCheckingPolicy>& other,
0172                              SourceLocation loc = SourceLocation::Current())
0173       : BasicCrossThreadPersistent(loc) {
0174     *this = other;
0175   }
0176 
0177   BasicCrossThreadPersistent(
0178       BasicCrossThreadPersistent&& other,
0179       SourceLocation loc = SourceLocation::Current()) noexcept {
0180     // Invoke operator=.
0181     *this = std::move(other);
0182   }
0183 
0184   BasicCrossThreadPersistent& operator=(
0185       const BasicCrossThreadPersistent& other) {
0186     PersistentRegionLock guard;
0187     AssignSafe(guard, other.Get());
0188     return *this;
0189   }
0190 
0191   template <typename U, typename OtherWeaknessPolicy,
0192             typename OtherLocationPolicy, typename OtherCheckingPolicy,
0193             typename = std::enable_if_t<std::is_base_of_v<T, U>>>
0194   BasicCrossThreadPersistent& operator=(
0195       const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
0196                                        OtherLocationPolicy,
0197                                        OtherCheckingPolicy>& other) {
0198     PersistentRegionLock guard;
0199     AssignSafe(guard, other.Get());
0200     return *this;
0201   }
0202 
0203   BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) {
0204     if (this == &other) return *this;
0205     Clear();
0206     PersistentRegionLock guard;
0207     PersistentBase::operator=(std::move(other));
0208     LocationPolicy::operator=(std::move(other));
0209     if (!IsValid(GetValue())) return *this;
0210     GetNode()->UpdateOwner(this);
0211     other.SetValue(nullptr);
0212     other.SetNode(nullptr);
0213     this->CheckPointer(Get());
0214     return *this;
0215   }
0216 
0217   /**
0218    * Assigns a raw pointer.
0219    *
0220    * Note: **Not thread-safe.**
0221    */
0222   BasicCrossThreadPersistent& operator=(T* other) {
0223     AssignUnsafe(other);
0224     return *this;
0225   }
0226 
0227   // Assignment from member.
0228   template <typename U, typename MemberBarrierPolicy,
0229             typename MemberWeaknessTag, typename MemberCheckingPolicy,
0230             typename MemberStorageType,
0231             typename = std::enable_if_t<std::is_base_of_v<T, U>>>
0232   BasicCrossThreadPersistent& operator=(
0233       internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
0234                             MemberCheckingPolicy, MemberStorageType>
0235           member) {
0236     return operator=(member.Get());
0237   }
0238 
0239   /**
0240    * Assigns a nullptr.
0241    *
0242    * \returns the handle.
0243    */
0244   BasicCrossThreadPersistent& operator=(std::nullptr_t) {
0245     Clear();
0246     return *this;
0247   }
0248 
0249   /**
0250    * Assigns the sentinel pointer.
0251    *
0252    * \returns the handle.
0253    */
0254   BasicCrossThreadPersistent& operator=(SentinelPointer s) {
0255     PersistentRegionLock guard;
0256     AssignSafe(guard, s);
0257     return *this;
0258   }
0259 
0260   /**
0261    * Returns a pointer to the stored object.
0262    *
0263    * Note: **Not thread-safe.**
0264    *
0265    * \returns a pointer to the stored object.
0266    */
0267   // CFI cast exemption to allow passing SentinelPointer through T* and support
0268   // heterogeneous assignments between different Member and Persistent handles
0269   // based on their actual types.
0270   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
0271     return static_cast<T*>(const_cast<void*>(GetValue()));
0272   }
0273 
0274   /**
0275    * Clears the stored object.
0276    */
0277   void Clear() {
0278     PersistentRegionLock guard;
0279     AssignSafe(guard, nullptr);
0280   }
0281 
0282   /**
0283    * Returns a pointer to the stored object and releases it.
0284    *
0285    * Note: **Not thread-safe.**
0286    *
0287    * \returns a pointer to the stored object.
0288    */
0289   T* Release() {
0290     T* result = Get();
0291     Clear();
0292     return result;
0293   }
0294 
0295   /**
0296    * Conversio to boolean.
0297    *
0298    * Note: **Not thread-safe.**
0299    *
0300    * \returns true if an actual object has been stored and false otherwise.
0301    */
0302   explicit operator bool() const { return Get(); }
0303 
0304   /**
0305    * Conversion to object of type T.
0306    *
0307    * Note: **Not thread-safe.**
0308    *
0309    * \returns the object.
0310    */
0311   operator T*() const { return Get(); }
0312 
0313   /**
0314    * Dereferences the stored object.
0315    *
0316    * Note: **Not thread-safe.**
0317    */
0318   T* operator->() const { return Get(); }
0319   T& operator*() const { return *Get(); }
0320 
0321   template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
0322             typename OtherLocationPolicy = LocationPolicy,
0323             typename OtherCheckingPolicy = CheckingPolicy>
0324   BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
0325                              OtherCheckingPolicy>
0326   To() const {
0327     using OtherBasicCrossThreadPersistent =
0328         BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
0329                                    OtherCheckingPolicy>;
0330     PersistentRegionLock guard;
0331     return OtherBasicCrossThreadPersistent(
0332         typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
0333         static_cast<U*>(Get()));
0334   }
0335 
0336   template <typename U = T,
0337             typename = std::enable_if_t<!BasicCrossThreadPersistent<
0338                 U, WeaknessPolicy>::IsStrongPersistent::value>>
0339   BasicCrossThreadPersistent<U, internal::StrongCrossThreadPersistentPolicy>
0340   Lock() const {
0341     return BasicCrossThreadPersistent<
0342         U, internal::StrongCrossThreadPersistentPolicy>(*this);
0343   }
0344 
0345  private:
0346   static bool IsValid(const void* ptr) {
0347     return ptr && ptr != kSentinelPointer;
0348   }
0349 
0350   static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
0351     root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr));
0352   }
0353 
0354   void AssignUnsafe(T* ptr) {
0355     const void* old_value = GetValue();
0356     if (IsValid(old_value)) {
0357       PersistentRegionLock guard;
0358       old_value = GetValue();
0359       // The fast path check (IsValid()) does not acquire the lock. Reload
0360       // the value to ensure the reference has not been cleared.
0361       if (IsValid(old_value)) {
0362         CrossThreadPersistentRegion& region =
0363             this->GetPersistentRegion(old_value);
0364         if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
0365           SetValue(ptr);
0366           this->CheckPointer(ptr);
0367           return;
0368         }
0369         region.FreeNode(GetNode());
0370         SetNode(nullptr);
0371       } else {
0372         CPPGC_DCHECK(!GetNode());
0373       }
0374     }
0375     SetValue(ptr);
0376     if (!IsValid(ptr)) return;
0377     PersistentRegionLock guard;
0378     SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
0379     this->CheckPointer(ptr);
0380   }
0381 
0382   void AssignSafe(PersistentRegionLock&, T* ptr) {
0383     PersistentRegionLock::AssertLocked();
0384     const void* old_value = GetValue();
0385     if (IsValid(old_value)) {
0386       CrossThreadPersistentRegion& region =
0387           this->GetPersistentRegion(old_value);
0388       if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
0389         SetValue(ptr);
0390         this->CheckPointer(ptr);
0391         return;
0392       }
0393       region.FreeNode(GetNode());
0394       SetNode(nullptr);
0395     }
0396     SetValue(ptr);
0397     if (!IsValid(ptr)) return;
0398     SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
0399     this->CheckPointer(ptr);
0400   }
0401 
0402   void ClearFromGC() const {
0403     if (IsValid(GetValueFromGC())) {
0404       WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
0405           .FreeNode(GetNodeFromGC());
0406       CrossThreadPersistentBase::ClearFromGC();
0407     }
0408   }
0409 
0410   // See Get() for details.
0411   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
0412   T* GetFromGC() const {
0413     return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
0414   }
0415 
0416   friend class internal::RootVisitor;
0417 };
0418 
0419 template <typename T, typename LocationPolicy, typename CheckingPolicy>
0420 struct IsWeak<
0421     BasicCrossThreadPersistent<T, internal::WeakCrossThreadPersistentPolicy,
0422                                LocationPolicy, CheckingPolicy>>
0423     : std::true_type {};
0424 
0425 }  // namespace internal
0426 
0427 namespace subtle {
0428 
0429 /**
0430  * **DO NOT USE: Has known caveats, see below.**
0431  *
0432  * CrossThreadPersistent allows retaining objects from threads other than the
0433  * thread the owning heap is operating on.
0434  *
0435  * Known caveats:
0436  * - Does not protect the heap owning an object from terminating.
0437  * - Reaching transitively through the graph is unsupported as objects may be
0438  *   moved concurrently on the thread owning the object.
0439  */
0440 template <typename T>
0441 using CrossThreadPersistent = internal::BasicCrossThreadPersistent<
0442     T, internal::StrongCrossThreadPersistentPolicy>;
0443 
0444 /**
0445  * **DO NOT USE: Has known caveats, see below.**
0446  *
0447  * CrossThreadPersistent allows weakly retaining objects from threads other than
0448  * the thread the owning heap is operating on.
0449  *
0450  * Known caveats:
0451  * - Does not protect the heap owning an object from terminating.
0452  * - Reaching transitively through the graph is unsupported as objects may be
0453  *   moved concurrently on the thread owning the object.
0454  */
0455 template <typename T>
0456 using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent<
0457     T, internal::WeakCrossThreadPersistentPolicy>;
0458 
0459 }  // namespace subtle
0460 }  // namespace cppgc
0461 
0462 #endif  // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_