tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ThreadSafeWeakPtr.h (10849B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* A thread-safe weak pointer */
      8 
      9 /**
     10 * Derive from SupportsThreadSafeWeakPtr to allow thread-safe weak pointers to
     11 * an atomically refcounted derived class. These thread-safe weak pointers may
     12 * be safely accessed and converted to strong pointers on multiple threads.
     13 *
     14 * Note that SupportsThreadSafeWeakPtr defines the same member functions as
     15 * AtomicRefCounted, so you should not separately inherit from it.
     16 *
     17 * ThreadSafeWeakPtr and its implementation is distinct from the normal WeakPtr
     18 * which is not thread-safe. The interface discipline and implementation details
     19 * are different enough that these two implementations are separated for now for
     20 * efficiency reasons. If you don't actually need to use weak pointers on
     21 * multiple threads, you can just use WeakPtr instead.
     22 *
     23 * When deriving from SupportsThreadSafeWeakPtr, you should add
     24 * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your
     25 * class, where ClassName is the name of your class.
     26 *
     27 * Example usage:
     28 *
     29 *   class C : public SupportsThreadSafeWeakPtr<C>
     30 *   {
     31 *   public:
     32 *     MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
     33 *     void doStuff();
     34 *   };
     35 *
     36 *   ThreadSafeWeakPtr<C> weak;
     37 *   {
     38 *     RefPtr<C> strong = new C;
     39 *     if (strong) {
     40 *       strong->doStuff();
     41 *     }
     42 *     // Make a new weak reference to the object from the strong reference.
     43 *     weak = strong;
     44 *   }
     45 *   MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all "
     46 *                           "strong references are released.");
     47 *
     48 *   // Convert the weak reference to a strong reference for usage.
     49 *   RefPtr<C> other(weak);
     50 *   if (other) {
     51 *     other->doStuff();
     52 *   }
     53 */
     54 
     55 #ifndef mozilla_ThreadSafeWeakPtr_h
     56 #define mozilla_ThreadSafeWeakPtr_h
     57 
     58 #include "mozilla/Assertions.h"
     59 #include "mozilla/RefCountType.h"
     60 #include "mozilla/RefCounted.h"
     61 #include "mozilla/RefPtr.h"
     62 
     63 namespace mozilla {
     64 
     65 template <typename T>
     66 class ThreadSafeWeakPtr;
     67 
     68 template <typename T>
     69 class SupportsThreadSafeWeakPtr;
     70 
     71 namespace detail {
     72 
     73 class SupportsThreadSafeWeakPtrBase {};
     74 
     75 // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr
     76 // object. This object owns the reference count for the tracked object, and can
     77 // perform atomic refcount upgrades.
     78 class ThreadSafeWeakReference
     79    : public external::AtomicRefCounted<ThreadSafeWeakReference> {
     80 public:
     81  explicit ThreadSafeWeakReference(SupportsThreadSafeWeakPtrBase* aPtr)
     82      : mPtr(aPtr) {}
     83 
     84 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
     85  const char* typeName() const { return "ThreadSafeWeakReference"; }
     86  size_t typeSize() const { return sizeof(*this); }
     87 #endif
     88 
     89 private:
     90  template <typename U>
     91  friend class mozilla::SupportsThreadSafeWeakPtr;
     92  template <typename U>
     93  friend class mozilla::ThreadSafeWeakPtr;
     94 
     95  // Number of strong references to the underlying data structure.
     96  //
     97  // Other than the initial strong `AddRef` call incrementing this value to 1,
     98  // which must occur before any weak references are taken, once this value
     99  // reaches `0` again it cannot be changed.
    100  RC<MozRefCountType, AtomicRefCount> mStrongCnt{0};
    101 
    102  // Raw pointer to the tracked object. It is never valid to read this value
    103  // outside of `ThreadSafeWeakPtr::getRefPtr()`.
    104  SupportsThreadSafeWeakPtrBase* MOZ_NON_OWNING_REF mPtr;
    105 };
    106 
    107 }  // namespace detail
    108 
    109 // For usage documentation for SupportsThreadSafeWeakPtr, see the header-level
    110 // documentation.
    111 //
    112 // To understand the layout of SupportsThreadSafeWeakPtr, consider the following
    113 // simplified declaration:
    114 //
    115 // class MyType: SupportsThreadSafeWeakPtr { uint32_t mMyData; ... }
    116 //
    117 // Which will result in the following layout:
    118 //
    119 //   +--------------------+
    120 //   | MyType             | <===============================================+
    121 //   +--------------------+                                                 I
    122 //   | RefPtr mWeakRef  o======> +-------------------------------------+    I
    123 //   | uint32_t mMyData   |      | ThreadSafeWeakReference             |    I
    124 //   +--------------------+      +-------------------------------------+    I
    125 //                               | RC mRefCount                        |    I
    126 //                               | RC mStrongCount                     |    I
    127 //                               | SupportsThreadSafeWeakPtrBase* mPtr o====+
    128 //                               +-------------------------------------+
    129 //
    130 // The mRefCount inherited from AtomicRefCounted<ThreadSafeWeakReference> is the
    131 // weak count. This means MyType implicitly holds a weak reference, so if the
    132 // weak count ever hits 0, we know all strong *and* weak references are gone,
    133 // and it's safe to free the ThreadSafeWeakReference. MyType's AddRef and
    134 // Release implementations otherwise only manipulate mStrongCount.
    135 //
    136 // It's necessary to keep the counts in a separate allocation because we need
    137 // to be able to delete MyType while weak references still exist. This ensures
    138 // that weak references can still access all the state necessary to check if
    139 // they can be upgraded (mStrongCount).
    140 template <typename T>
    141 class SupportsThreadSafeWeakPtr : public detail::SupportsThreadSafeWeakPtrBase {
    142 protected:
    143  using ThreadSafeWeakReference = detail::ThreadSafeWeakReference;
    144 
    145  // The `this` pointer will not have subclasses initialized yet, but it will
    146  // also not be read until a weak pointer is upgraded, which should be after
    147  // this point.
    148  SupportsThreadSafeWeakPtr() : mWeakRef(new ThreadSafeWeakReference(this)) {
    149    static_assert(std::is_base_of_v<SupportsThreadSafeWeakPtr, T>,
    150                  "T must derive from SupportsThreadSafeWeakPtr");
    151  }
    152 
    153 public:
    154  // Compatibility with RefPtr
    155  MozExternalRefCountType AddRef() const {
    156    auto& refCnt = mWeakRef->mStrongCnt;
    157    MOZ_ASSERT(int32_t(refCnt) >= 0);
    158    MozRefCountType cnt = ++refCnt;
    159    detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
    160    return cnt;
    161  }
    162 
    163  MozExternalRefCountType Release() const {
    164    auto& refCnt = mWeakRef->mStrongCnt;
    165    MOZ_ASSERT(int32_t(refCnt) > 0);
    166    detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
    167    MozRefCountType cnt = --refCnt;
    168    logger.logRelease(cnt);
    169    if (0 == cnt) {
    170      // Because we have atomically decremented the refcount above, only one
    171      // thread can get a 0 count here. Thus, it is safe to access and destroy
    172      // |this| here.
    173      // No other thread can acquire a strong reference to |this| anymore
    174      // through our weak pointer, as upgrading a weak pointer always uses
    175      // |IncrementIfNonzero|, meaning the refcount can't leave a zero reference
    176      // state.
    177      // NOTE: We can't update our refcount to the marker `DEAD` value here, as
    178      // it may still be read by mWeakRef.
    179      delete static_cast<const T*>(this);
    180    }
    181    return cnt;
    182  }
    183 
    184  using HasThreadSafeRefCnt = std::true_type;
    185 
    186  // Compatibility with wtf::RefPtr
    187  void ref() { AddRef(); }
    188  void deref() { Release(); }
    189  MozRefCountType refCount() const { return mWeakRef->mStrongCnt; }
    190  bool hasOneRef() const { return refCount() == 1; }
    191 
    192 private:
    193  template <typename U>
    194  friend class ThreadSafeWeakPtr;
    195 
    196  ThreadSafeWeakReference* getThreadSafeWeakReference() const {
    197    return mWeakRef;
    198  }
    199 
    200  const RefPtr<ThreadSafeWeakReference> mWeakRef;
    201 };
    202 
    203 // A thread-safe variant of a weak pointer
    204 template <typename T>
    205 class ThreadSafeWeakPtr {
    206  using ThreadSafeWeakReference = detail::ThreadSafeWeakReference;
    207 
    208 public:
    209  ThreadSafeWeakPtr() = default;
    210 
    211  ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default;
    212  ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default;
    213 
    214  ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default;
    215  ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default;
    216 
    217  ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther) {
    218    if (aOther) {
    219      // Get the underlying shared weak reference to the object.
    220      mRef = aOther->getThreadSafeWeakReference();
    221    } else {
    222      mRef = nullptr;
    223    }
    224    return *this;
    225  }
    226 
    227  explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther) { *this = aOther; }
    228 
    229  ThreadSafeWeakPtr& operator=(decltype(nullptr)) {
    230    mRef = nullptr;
    231    return *this;
    232  }
    233 
    234  explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
    235 
    236  // Use the explicit `IsNull()` or `IsDead()` methods instead.
    237  explicit operator bool() const = delete;
    238 
    239  // Check if the ThreadSafeWeakPtr was created wrapping a null pointer.
    240  bool IsNull() const { return !mRef; }
    241 
    242  // Check if the managed object is nullptr or has already been destroyed. Once
    243  // IsDead returns true, this ThreadSafeWeakPtr can never be upgraded again
    244  // (until it has been re-assigned), but a false return value does NOT imply
    245  // that any future upgrade will be successful.
    246  bool IsDead() const { return IsNull() || size_t(mRef->mStrongCnt) == 0; }
    247 
    248  bool operator==(const ThreadSafeWeakPtr& aOther) const {
    249    return mRef == aOther.mRef;
    250  }
    251 
    252  bool operator==(const RefPtr<T>& aOther) const {
    253    return *this == aOther.get();
    254  }
    255 
    256  friend bool operator==(const RefPtr<T>& aStrong,
    257                         const ThreadSafeWeakPtr& aWeak) {
    258    return aWeak == aStrong.get();
    259  }
    260 
    261  bool operator==(const T* aOther) const {
    262    if (!mRef) {
    263      return !aOther;
    264    }
    265    return aOther && aOther->getThreadSafeWeakReference() == mRef;
    266  }
    267 
    268  template <typename U>
    269  bool operator!=(const U& aOther) const {
    270    return !(*this == aOther);
    271  }
    272 
    273  // Convert the weak pointer to a strong RefPtr.
    274  explicit operator RefPtr<T>() const { return getRefPtr(); }
    275 
    276 private:
    277  // Gets a new strong reference of the proper type T to the tracked object.
    278  already_AddRefed<T> getRefPtr() const {
    279    if (!mRef) {
    280      return nullptr;
    281    }
    282    // Increment our strong reference count only if it is nonzero, meaning that
    283    // the object is still alive.
    284    MozRefCountType cnt = mRef->mStrongCnt.IncrementIfNonzero();
    285    if (cnt == 0) {
    286      return nullptr;
    287    }
    288 
    289    RefPtr<T> ptr = already_AddRefed<T>(static_cast<T*>(mRef->mPtr));
    290    detail::RefCountLogger::logAddRef(ptr.get(), cnt);
    291    return ptr.forget();
    292  }
    293 
    294  // A shared weak reference to an object. Note that this may be null so as to
    295  // save memory (at the slight cost of an extra null check) if no object is
    296  // being tracked.
    297  RefPtr<ThreadSafeWeakReference> mRef;
    298 };
    299 
    300 }  // namespace mozilla
    301 
    302 template <typename T>
    303 inline already_AddRefed<T> do_AddRef(
    304    const mozilla::ThreadSafeWeakPtr<T>& aObj) {
    305  RefPtr<T> ref(aObj);
    306  return ref.forget();
    307 }
    308 
    309 #endif /* mozilla_ThreadSafeWeakPtr_h */