Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-02 08:54:42

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(
0106       const SourceLocation& loc = SourceLocation::Current())
0107       : LocationPolicy(loc) {}
0108 
0109   BasicCrossThreadPersistent(
0110       std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())
0111       : LocationPolicy(loc) {}
0112 
0113   BasicCrossThreadPersistent(
0114       SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
0115       : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
0116 
0117   BasicCrossThreadPersistent(
0118       T* raw, const SourceLocation& loc = SourceLocation::Current())
0119       : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
0120     if (!IsValid(raw)) return;
0121     PersistentRegionLock guard;
0122     CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
0123     SetNode(region.AllocateNode(this, &TraceAsRoot));
0124     this->CheckPointer(raw);
0125   }
0126 
0127   class UnsafeCtorTag {
0128    private:
0129     UnsafeCtorTag() = default;
0130     template <typename U, typename OtherWeaknessPolicy,
0131               typename OtherLocationPolicy, typename OtherCheckingPolicy>
0132     friend class BasicCrossThreadPersistent;
0133   };
0134 
0135   BasicCrossThreadPersistent(
0136       UnsafeCtorTag, T* raw,
0137       const SourceLocation& loc = SourceLocation::Current())
0138       : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
0139     if (!IsValid(raw)) return;
0140     CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
0141     SetNode(region.AllocateNode(this, &TraceAsRoot));
0142     this->CheckPointer(raw);
0143   }
0144 
0145   BasicCrossThreadPersistent(
0146       T& raw, const SourceLocation& loc = SourceLocation::Current())
0147       : BasicCrossThreadPersistent(&raw, loc) {}
0148 
0149   template <typename U, typename MemberBarrierPolicy,
0150             typename MemberWeaknessTag, typename MemberCheckingPolicy,
0151             typename MemberStorageType,
0152             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
0153   BasicCrossThreadPersistent(
0154       internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
0155                             MemberCheckingPolicy, MemberStorageType>
0156           member,
0157       const SourceLocation& loc = SourceLocation::Current())
0158       : BasicCrossThreadPersistent(member.Get(), loc) {}
0159 
0160   BasicCrossThreadPersistent(
0161       const BasicCrossThreadPersistent& other,
0162       const SourceLocation& loc = SourceLocation::Current())
0163       : BasicCrossThreadPersistent(loc) {
0164     // Invoke operator=.
0165     *this = other;
0166   }
0167 
0168   // Heterogeneous ctor.
0169   template <typename U, typename OtherWeaknessPolicy,
0170             typename OtherLocationPolicy, typename OtherCheckingPolicy,
0171             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
0172   BasicCrossThreadPersistent(
0173       const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
0174                                        OtherLocationPolicy,
0175                                        OtherCheckingPolicy>& other,
0176       const SourceLocation& loc = SourceLocation::Current())
0177       : BasicCrossThreadPersistent(loc) {
0178     *this = other;
0179   }
0180 
0181   BasicCrossThreadPersistent(
0182       BasicCrossThreadPersistent&& other,
0183       const SourceLocation& loc = SourceLocation::Current()) noexcept {
0184     // Invoke operator=.
0185     *this = std::move(other);
0186   }
0187 
0188   BasicCrossThreadPersistent& operator=(
0189       const BasicCrossThreadPersistent& other) {
0190     PersistentRegionLock guard;
0191     AssignSafe(guard, other.Get());
0192     return *this;
0193   }
0194 
0195   template <typename U, typename OtherWeaknessPolicy,
0196             typename OtherLocationPolicy, typename OtherCheckingPolicy,
0197             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
0198   BasicCrossThreadPersistent& operator=(
0199       const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
0200                                        OtherLocationPolicy,
0201                                        OtherCheckingPolicy>& other) {
0202     PersistentRegionLock guard;
0203     AssignSafe(guard, other.Get());
0204     return *this;
0205   }
0206 
0207   BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) {
0208     if (this == &other) return *this;
0209     Clear();
0210     PersistentRegionLock guard;
0211     PersistentBase::operator=(std::move(other));
0212     LocationPolicy::operator=(std::move(other));
0213     if (!IsValid(GetValue())) return *this;
0214     GetNode()->UpdateOwner(this);
0215     other.SetValue(nullptr);
0216     other.SetNode(nullptr);
0217     this->CheckPointer(Get());
0218     return *this;
0219   }
0220 
0221   /**
0222    * Assigns a raw pointer.
0223    *
0224    * Note: **Not thread-safe.**
0225    */
0226   BasicCrossThreadPersistent& operator=(T* other) {
0227     AssignUnsafe(other);
0228     return *this;
0229   }
0230 
0231   // Assignment from member.
0232   template <typename U, typename MemberBarrierPolicy,
0233             typename MemberWeaknessTag, typename MemberCheckingPolicy,
0234             typename MemberStorageType,
0235             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
0236   BasicCrossThreadPersistent& operator=(
0237       internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
0238                             MemberCheckingPolicy, MemberStorageType>
0239           member) {
0240     return operator=(member.Get());
0241   }
0242 
0243   /**
0244    * Assigns a nullptr.
0245    *
0246    * \returns the handle.
0247    */
0248   BasicCrossThreadPersistent& operator=(std::nullptr_t) {
0249     Clear();
0250     return *this;
0251   }
0252 
0253   /**
0254    * Assigns the sentinel pointer.
0255    *
0256    * \returns the handle.
0257    */
0258   BasicCrossThreadPersistent& operator=(SentinelPointer s) {
0259     PersistentRegionLock guard;
0260     AssignSafe(guard, s);
0261     return *this;
0262   }
0263 
0264   /**
0265    * Returns a pointer to the stored object.
0266    *
0267    * Note: **Not thread-safe.**
0268    *
0269    * \returns a pointer to the stored object.
0270    */
0271   // CFI cast exemption to allow passing SentinelPointer through T* and support
0272   // heterogeneous assignments between different Member and Persistent handles
0273   // based on their actual types.
0274   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
0275     return static_cast<T*>(const_cast<void*>(GetValue()));
0276   }
0277 
0278   /**
0279    * Clears the stored object.
0280    */
0281   void Clear() {
0282     PersistentRegionLock guard;
0283     AssignSafe(guard, nullptr);
0284   }
0285 
0286   /**
0287    * Returns a pointer to the stored object and releases it.
0288    *
0289    * Note: **Not thread-safe.**
0290    *
0291    * \returns a pointer to the stored object.
0292    */
0293   T* Release() {
0294     T* result = Get();
0295     Clear();
0296     return result;
0297   }
0298 
0299   /**
0300    * Conversio to boolean.
0301    *
0302    * Note: **Not thread-safe.**
0303    *
0304    * \returns true if an actual object has been stored and false otherwise.
0305    */
0306   explicit operator bool() const { return Get(); }
0307 
0308   /**
0309    * Conversion to object of type T.
0310    *
0311    * Note: **Not thread-safe.**
0312    *
0313    * \returns the object.
0314    */
0315   operator T*() const { return Get(); }
0316 
0317   /**
0318    * Dereferences the stored object.
0319    *
0320    * Note: **Not thread-safe.**
0321    */
0322   T* operator->() const { return Get(); }
0323   T& operator*() const { return *Get(); }
0324 
0325   template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
0326             typename OtherLocationPolicy = LocationPolicy,
0327             typename OtherCheckingPolicy = CheckingPolicy>
0328   BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
0329                              OtherCheckingPolicy>
0330   To() const {
0331     using OtherBasicCrossThreadPersistent =
0332         BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
0333                                    OtherCheckingPolicy>;
0334     PersistentRegionLock guard;
0335     return OtherBasicCrossThreadPersistent(
0336         typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
0337         static_cast<U*>(Get()));
0338   }
0339 
0340   template <typename U = T,
0341             typename = typename std::enable_if<!BasicCrossThreadPersistent<
0342                 U, WeaknessPolicy>::IsStrongPersistent::value>::type>
0343   BasicCrossThreadPersistent<U, internal::StrongCrossThreadPersistentPolicy>
0344   Lock() const {
0345     return BasicCrossThreadPersistent<
0346         U, internal::StrongCrossThreadPersistentPolicy>(*this);
0347   }
0348 
0349  private:
0350   static bool IsValid(const void* ptr) {
0351     return ptr && ptr != kSentinelPointer;
0352   }
0353 
0354   static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
0355     root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr));
0356   }
0357 
0358   void AssignUnsafe(T* ptr) {
0359     const void* old_value = GetValue();
0360     if (IsValid(old_value)) {
0361       PersistentRegionLock guard;
0362       old_value = GetValue();
0363       // The fast path check (IsValid()) does not acquire the lock. Reload
0364       // the value to ensure the reference has not been cleared.
0365       if (IsValid(old_value)) {
0366         CrossThreadPersistentRegion& region =
0367             this->GetPersistentRegion(old_value);
0368         if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
0369           SetValue(ptr);
0370           this->CheckPointer(ptr);
0371           return;
0372         }
0373         region.FreeNode(GetNode());
0374         SetNode(nullptr);
0375       } else {
0376         CPPGC_DCHECK(!GetNode());
0377       }
0378     }
0379     SetValue(ptr);
0380     if (!IsValid(ptr)) return;
0381     PersistentRegionLock guard;
0382     SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
0383     this->CheckPointer(ptr);
0384   }
0385 
0386   void AssignSafe(PersistentRegionLock&, T* ptr) {
0387     PersistentRegionLock::AssertLocked();
0388     const void* old_value = GetValue();
0389     if (IsValid(old_value)) {
0390       CrossThreadPersistentRegion& region =
0391           this->GetPersistentRegion(old_value);
0392       if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
0393         SetValue(ptr);
0394         this->CheckPointer(ptr);
0395         return;
0396       }
0397       region.FreeNode(GetNode());
0398       SetNode(nullptr);
0399     }
0400     SetValue(ptr);
0401     if (!IsValid(ptr)) return;
0402     SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
0403     this->CheckPointer(ptr);
0404   }
0405 
0406   void ClearFromGC() const {
0407     if (IsValid(GetValueFromGC())) {
0408       WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
0409           .FreeNode(GetNodeFromGC());
0410       CrossThreadPersistentBase::ClearFromGC();
0411     }
0412   }
0413 
0414   // See Get() for details.
0415   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
0416   T* GetFromGC() const {
0417     return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
0418   }
0419 
0420   friend class internal::RootVisitor;
0421 };
0422 
0423 template <typename T, typename LocationPolicy, typename CheckingPolicy>
0424 struct IsWeak<
0425     BasicCrossThreadPersistent<T, internal::WeakCrossThreadPersistentPolicy,
0426                                LocationPolicy, CheckingPolicy>>
0427     : std::true_type {};
0428 
0429 }  // namespace internal
0430 
0431 namespace subtle {
0432 
0433 /**
0434  * **DO NOT USE: Has known caveats, see below.**
0435  *
0436  * CrossThreadPersistent allows retaining objects from threads other than the
0437  * thread the owning heap is operating on.
0438  *
0439  * Known caveats:
0440  * - Does not protect the heap owning an object from terminating.
0441  * - Reaching transitively through the graph is unsupported as objects may be
0442  *   moved concurrently on the thread owning the object.
0443  */
0444 template <typename T>
0445 using CrossThreadPersistent = internal::BasicCrossThreadPersistent<
0446     T, internal::StrongCrossThreadPersistentPolicy>;
0447 
0448 /**
0449  * **DO NOT USE: Has known caveats, see below.**
0450  *
0451  * CrossThreadPersistent allows weakly retaining objects from threads other than
0452  * the thread the owning heap is operating on.
0453  *
0454  * Known caveats:
0455  * - Does not protect the heap owning an object from terminating.
0456  * - Reaching transitively through the graph is unsupported as objects may be
0457  *   moved concurrently on the thread owning the object.
0458  */
0459 template <typename T>
0460 using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent<
0461     T, internal::WeakCrossThreadPersistentPolicy>;
0462 
0463 }  // namespace subtle
0464 }  // namespace cppgc
0465 
0466 #endif  // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_