tor-browser

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

SafeRefPtr.h (14833B)


      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 #ifndef mozilla_saferefptr_h__
      8 #define mozilla_saferefptr_h__
      9 
     10 #include "mozilla/ArrayAlgorithm.h"
     11 #include "mozilla/Maybe.h"
     12 #include "mozilla/RefCounted.h"
     13 #include "mozilla/RefPtr.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsTObserverArray.h"
     16 
     17 namespace mozilla {
     18 template <typename T>
     19 class SafeRefPtr;
     20 
     21 template <typename T, typename... Args>
     22 SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs);
     23 
     24 namespace detail {
     25 struct InitialConstructionTag {};
     26 
     27 class SafeRefCountedBase {
     28  template <typename U, typename... Args>
     29  friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs);
     30 
     31  template <typename T>
     32  friend class SafeRefPtr;
     33 
     34  void* operator new(size_t aSize) { return ::operator new(aSize); }
     35 
     36 protected:
     37  void operator delete(void* aPtr) { ::operator delete(aPtr); }
     38 
     39 public:
     40  void* operator new[](size_t) = delete;
     41 };
     42 
     43 // SafeRefCounted is similar to RefCounted, but they differ in their initial
     44 // refcount (here 1), and the visibility of operator new (here private). The
     45 // rest is mostly a copy of RefCounted.
     46 template <typename T, RefCountAtomicity Atomicity>
     47 class SafeRefCounted : public SafeRefCountedBase {
     48 protected:
     49  SafeRefCounted() = default;
     50 #ifdef DEBUG
     51  ~SafeRefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
     52 #endif
     53 
     54 public:
     55  // Compatibility with nsRefPtr.
     56  MozRefCountType AddRef() const {
     57    // Note: this method must be thread safe for AtomicRefCounted.
     58    MOZ_ASSERT(int32_t(mRefCnt) >= 0);
     59    const MozRefCountType cnt = ++mRefCnt;
     60    detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
     61    return cnt;
     62  }
     63 
     64  MozRefCountType Release() const {
     65    // Note: this method must be thread safe for AtomicRefCounted.
     66    MOZ_ASSERT(int32_t(mRefCnt) > 0);
     67    detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
     68    const MozRefCountType cnt = --mRefCnt;
     69    // Note: it's not safe to touch |this| after decrementing the refcount,
     70    // except for below.
     71    logger.logRelease(cnt);
     72    if (0 == cnt) {
     73      // Because we have atomically decremented the refcount above, only
     74      // one thread can get a 0 count here, so as long as we can assume that
     75      // everything else in the system is accessing this object through
     76      // RefPtrs, it's safe to access |this| here.
     77 #ifdef DEBUG
     78      mRefCnt = detail::DEAD;
     79 #endif
     80      delete static_cast<const T*>(this);
     81    }
     82    return cnt;
     83  }
     84 
     85  // Compatibility with wtf::RefPtr.
     86  void ref() { AddRef(); }
     87  void deref() { Release(); }
     88  MozRefCountType refCount() const { return mRefCnt; }
     89  bool hasOneRef() const {
     90    MOZ_ASSERT(mRefCnt > 0);
     91    return mRefCnt == 1;
     92  }
     93 
     94 protected:
     95  SafeRefPtr<T> SafeRefPtrFromThis();
     96 
     97 private:
     98  mutable RC<MozRefCountType, Atomicity> mRefCnt =
     99      RC<MozRefCountType, Atomicity>{1};
    100 };
    101 }  // namespace detail
    102 
    103 template <typename T>
    104 class SafeRefCounted
    105    : public detail::SafeRefCounted<T, detail::NonAtomicRefCount> {
    106 public:
    107  ~SafeRefCounted() {
    108    static_assert(std::is_base_of<SafeRefCounted, T>::value,
    109                  "T must derive from SafeRefCounted<T>");
    110  }
    111 };
    112 
    113 template <typename T>
    114 class AtomicSafeRefCounted
    115    : public detail::SafeRefCounted<T, detail::AtomicRefCount> {
    116 public:
    117  ~AtomicSafeRefCounted() {
    118    static_assert(std::is_base_of<AtomicSafeRefCounted, T>::value,
    119                  "T must derive from AtomicSafeRefCounted<T>");
    120  }
    121 };
    122 
    123 struct AcquireStrongRefFromRawPtr {};
    124 
    125 // XXX for Apple, clang::trivial_abi is probably also supported, but we need to
    126 // find out the correct version number
    127 #if defined(__clang__) && !defined(__apple_build_version__) && \
    128    __clang_major__ >= 7
    129 #  define MOZ_TRIVIAL_ABI [[clang::trivial_abi]]
    130 #else
    131 #  define MOZ_TRIVIAL_ABI
    132 #endif
    133 
    134 // A restricted variant of mozilla::RefPtr<T>, which prohibits some unsafe or
    135 // unperformant misuses, in particular:
    136 // * It is not implicitly convertible from a raw pointer. Unsafe acquisitions
    137 //   from a raw pointer must be made using the verbose
    138 //   AcquireStrongRefFromRawPtr. To create a new object on the heap, use
    139 //   MakeSafeRefPtr.
    140 // * It does not implicitly decay to a raw pointer. unsafeGetRawPtr() must be
    141 // called
    142 //   explicitly.
    143 // * It is not copyable, but must be explicitly copied using clonePtr().
    144 // * Temporaries cannot be dereferenced using operator* or operator->.
    145 template <typename T>
    146 class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr {
    147  template <typename U>
    148  friend class SafeRefPtr;
    149 
    150  template <typename U, typename... Args>
    151  friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs);
    152 
    153  T* MOZ_OWNING_REF mRawPtr = nullptr;
    154 
    155  // BEGIN Some things copied from RefPtr.
    156  // We cannot simply use a RefPtr member because we want to be trivial_abi,
    157  // which RefPtr is not.
    158  void assign_with_AddRef(T* aRawPtr) {
    159    if (aRawPtr) {
    160      ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr);
    161    }
    162    assign_assuming_AddRef(aRawPtr);
    163  }
    164 
    165  void assign_assuming_AddRef(T* aNewPtr) {
    166    T* oldPtr = mRawPtr;
    167    mRawPtr = aNewPtr;
    168    if (oldPtr) {
    169      ConstRemovingRefPtrTraits<T>::Release(oldPtr);
    170    }
    171  }
    172 
    173  template <class U>
    174  struct ConstRemovingRefPtrTraits {
    175    static void AddRef(U* aPtr) { mozilla::RefPtrTraits<U>::AddRef(aPtr); }
    176    static void Release(U* aPtr) { mozilla::RefPtrTraits<U>::Release(aPtr); }
    177  };
    178  template <class U>
    179  struct ConstRemovingRefPtrTraits<const U> {
    180    static void AddRef(const U* aPtr) {
    181      mozilla::RefPtrTraits<U>::AddRef(const_cast<U*>(aPtr));
    182    }
    183    static void Release(const U* aPtr) {
    184      mozilla::RefPtrTraits<U>::Release(const_cast<U*>(aPtr));
    185    }
    186  };
    187  // END Some things copied from RefPtr.
    188 
    189  SafeRefPtr(T* aRawPtr, mozilla::detail::InitialConstructionTag);
    190 
    191 public:
    192  SafeRefPtr() = default;
    193 
    194  template <typename U,
    195            typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
    196  MOZ_IMPLICIT SafeRefPtr(SafeRefPtr<U>&& aSrc) : mRawPtr(aSrc.mRawPtr) {
    197    aSrc.mRawPtr = nullptr;
    198  }
    199 
    200  explicit SafeRefPtr(RefPtr<T>&& aRefPtr) : mRawPtr(aRefPtr.forget().take()) {}
    201 
    202  // To prevent implicit conversion of raw pointer to RefPtr and then
    203  // calling the previous overload.
    204  SafeRefPtr(T* const aRawPtr) = delete;
    205 
    206  SafeRefPtr(T* const aRawPtr, const AcquireStrongRefFromRawPtr&) {
    207    assign_with_AddRef(aRawPtr);
    208  }
    209 
    210  MOZ_IMPLICIT SafeRefPtr(std::nullptr_t) {}
    211 
    212  // Prevent implicit copying, use clonePtr() instead.
    213  SafeRefPtr(const SafeRefPtr&) = delete;
    214  SafeRefPtr& operator=(const SafeRefPtr&) = delete;
    215 
    216  // Allow moving.
    217  SafeRefPtr(SafeRefPtr&& aOther) noexcept : mRawPtr(aOther.mRawPtr) {
    218    aOther.mRawPtr = nullptr;
    219  }
    220  SafeRefPtr& operator=(SafeRefPtr&& aOther) noexcept {
    221    assign_assuming_AddRef(aOther.forget().take());
    222    return *this;
    223  }
    224 
    225  ~SafeRefPtr() {
    226    static_assert(!std::is_copy_constructible_v<T>);
    227    static_assert(!std::is_copy_assignable_v<T>);
    228    static_assert(!std::is_move_constructible_v<T>);
    229    static_assert(!std::is_move_assignable_v<T>);
    230 
    231    if (mRawPtr) {
    232      ConstRemovingRefPtrTraits<T>::Release(mRawPtr);
    233    }
    234  }
    235 
    236  typedef T element_type;
    237 
    238  explicit operator bool() const { return mRawPtr; }
    239  bool operator!() const { return !mRawPtr; }
    240 
    241  T& operator*() const&& = delete;
    242 
    243  T& operator*() const& {
    244    MOZ_ASSERT(mRawPtr);
    245    return *mRawPtr;
    246  }
    247 
    248  T* operator->() const&& = delete;
    249 
    250  T* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN {
    251    MOZ_ASSERT(mRawPtr);
    252    return mRawPtr;
    253  }
    254 
    255  Maybe<T&> maybeDeref() const { return ToMaybeRef(mRawPtr); }
    256 
    257  T* unsafeGetRawPtr() const { return mRawPtr; }
    258 
    259  SafeRefPtr<T> clonePtr() const {
    260    return SafeRefPtr{mRawPtr, AcquireStrongRefFromRawPtr{}};
    261  }
    262 
    263  already_AddRefed<T> forget() {
    264    auto* const res = mRawPtr;
    265    mRawPtr = nullptr;
    266    return dont_AddRef(res);
    267  }
    268 
    269  bool operator==(const SafeRefPtr<T>& aOther) const {
    270    return mRawPtr == aOther.mRawPtr;
    271  }
    272 
    273  bool operator!=(const SafeRefPtr<T>& aOther) const {
    274    return mRawPtr != aOther.mRawPtr;
    275  }
    276 
    277  template <typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
    278  SafeRefPtr<U> downcast() && {
    279    SafeRefPtr<U> res;
    280    res.mRawPtr = static_cast<U*>(mRawPtr);
    281    mRawPtr = nullptr;
    282    return res;
    283  }
    284 
    285  template <typename U>
    286  friend RefPtr<U> AsRefPtr(SafeRefPtr<U>&& aSafeRefPtr);
    287 };
    288 
    289 template <typename T>
    290 SafeRefPtr(RefPtr<T>&&) -> SafeRefPtr<T>;
    291 
    292 template <typename T>
    293 SafeRefPtr(already_AddRefed<T>&&) -> SafeRefPtr<T>;
    294 
    295 template <typename T>
    296 class CheckedUnsafePtr;
    297 
    298 template <typename T>
    299 SafeRefPtr(const CheckedUnsafePtr<T>&, const AcquireStrongRefFromRawPtr&)
    300    -> SafeRefPtr<T>;
    301 
    302 template <typename T>
    303 SafeRefPtr<T>::SafeRefPtr(T* aRawPtr, detail::InitialConstructionTag)
    304    : mRawPtr(aRawPtr) {
    305  if (!std::is_base_of_v<detail::SafeRefCountedBase, T> && mRawPtr) {
    306    ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
    307  }
    308 }
    309 
    310 template <typename T>
    311 bool operator==(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) {
    312  return !aRhs;
    313 }
    314 
    315 template <typename T>
    316 bool operator!=(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) {
    317  return static_cast<bool>(aRhs);
    318 }
    319 
    320 template <typename T>
    321 bool operator==(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) {
    322  return !aLhs;
    323 }
    324 
    325 template <typename T>
    326 bool operator!=(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) {
    327  return static_cast<bool>(aLhs);
    328 }
    329 
    330 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    331 bool operator==(T* const aLhs, const SafeRefPtr<U>& aRhs) {
    332  return aLhs == aRhs.unsafeGetRawPtr();
    333 }
    334 
    335 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    336 bool operator!=(T* const aLhs, const SafeRefPtr<U>& aRhs) {
    337  return !(aLhs == aRhs);
    338 }
    339 
    340 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    341 bool operator==(const SafeRefPtr<T>& aLhs, U* const aRhs) {
    342  return aRhs == aLhs;
    343 }
    344 
    345 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    346 bool operator!=(const SafeRefPtr<T>& aLhs, U* const aRhs) {
    347  return aRhs != aLhs;
    348 }
    349 
    350 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    351 bool operator==(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) {
    352  return &aLhs.ref() == aRhs.unsafeGetRawPtr();
    353 }
    354 
    355 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    356 bool operator!=(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) {
    357  return !(aLhs == aRhs);
    358 }
    359 
    360 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    361 bool operator==(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) {
    362  return aRhs == aLhs;
    363 }
    364 
    365 template <typename T, typename U, typename = std::common_type_t<T*, U*>>
    366 bool operator!=(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) {
    367  return aRhs != aLhs;
    368 }
    369 
    370 template <typename T>
    371 RefPtr<T> AsRefPtr(SafeRefPtr<T>&& aSafeRefPtr) {
    372  return aSafeRefPtr.forget();
    373 }
    374 
    375 template <typename T, typename... Args>
    376 SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs) {
    377  return SafeRefPtr{new T(std::forward<Args>(aArgs)...),
    378                    detail::InitialConstructionTag{}};
    379 }
    380 
    381 template <typename T>
    382 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
    383                                 const SafeRefPtr<T>& aField, const char* aName,
    384                                 uint32_t aFlags = 0) {
    385  CycleCollectionNoteChild(aCallback, aField.unsafeGetRawPtr(), aName, aFlags);
    386 }
    387 
    388 template <typename T>
    389 void ImplCycleCollectionUnlink(SafeRefPtr<T>& aField) {
    390  aField = nullptr;
    391 }
    392 
    393 namespace detail {
    394 
    395 template <typename T, RefCountAtomicity Atomicity>
    396 SafeRefPtr<T> SafeRefCounted<T, Atomicity>::SafeRefPtrFromThis() {
    397  // this actually is safe
    398  return {static_cast<T*>(this), AcquireStrongRefFromRawPtr{}};
    399 }
    400 
    401 template <typename T>
    402 struct CopyablePtr<SafeRefPtr<T>> {
    403  SafeRefPtr<T> mPtr;
    404 
    405  explicit CopyablePtr(SafeRefPtr<T> aPtr) : mPtr{std::move(aPtr)} {}
    406 
    407  CopyablePtr(const CopyablePtr& aOther) : mPtr{aOther.mPtr.clonePtr()} {}
    408  CopyablePtr& operator=(const CopyablePtr& aOther) {
    409    if (this != &aOther) {
    410      mPtr = aOther.mPtr.clonePtr();
    411    }
    412    return *this;
    413  }
    414  CopyablePtr(CopyablePtr&&) = default;
    415  CopyablePtr& operator=(CopyablePtr&&) = default;
    416 };
    417 
    418 }  // namespace detail
    419 
    420 namespace dom {
    421 /// XXX Move this to BindingUtils.h later on
    422 template <class T, class S>
    423 inline RefPtr<T> StrongOrRawPtr(SafeRefPtr<S>&& aPtr) {
    424  return AsRefPtr(std::move(aPtr));
    425 }
    426 
    427 }  // namespace dom
    428 
    429 }  // namespace mozilla
    430 
    431 template <class T>
    432 class nsTObserverArray<mozilla::SafeRefPtr<T>>
    433    : public nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0> {
    434 public:
    435  using base_type = nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0>;
    436  using size_type = nsTObserverArray_base::size_type;
    437 
    438  // Initialization methods
    439  nsTObserverArray() = default;
    440 
    441  // Initialize this array and pre-allocate some number of elements.
    442  explicit nsTObserverArray(size_type aCapacity) {
    443    base_type::mArray.SetCapacity(aCapacity);
    444  }
    445 
    446  nsTObserverArray Clone() const {
    447    auto result = nsTObserverArray{};
    448    result.mArray = mozilla::TransformIntoNewArray(
    449        this->mArray, [](const auto& ptr) { return ptr.clonePtr(); });
    450    return result;
    451  }
    452 };
    453 
    454 // Use MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED in a 'Class' derived from a
    455 // 'Super' class which derives from (Atomic)SafeRefCounted, and from some other
    456 // class using NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING.
    457 #if defined(NS_BUILD_REFCNT_LOGGING)
    458 #  define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super)         \
    459    template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
    460    friend class ::mozilla::detail::SafeRefCounted;                       \
    461    NS_IMETHOD_(MozExternalRefCountType) AddRef() override {              \
    462      NS_IMPL_ADDREF_INHERITED_GUTS(Class, Super);                        \
    463    }                                                                     \
    464    NS_IMETHOD_(MozExternalRefCountType) Release() override {             \
    465      NS_IMPL_RELEASE_INHERITED_GUTS(Class, Super);                       \
    466    }
    467 #else  // NS_BUILD_REFCNT_LOGGING
    468 #  define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super)         \
    469    template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
    470    friend class ::mozilla::detail::SafeRefCounted;                       \
    471    NS_IMETHOD_(MozExternalRefCountType) AddRef() override {              \
    472      return Super::AddRef();                                             \
    473    }                                                                     \
    474    NS_IMETHOD_(MozExternalRefCountType) Release() override {             \
    475      return Super::Release();                                            \
    476    }
    477 #endif
    478 
    479 #endif