tor-browser

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

AlreadyAddRefed.h (6300B)


      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 /* Typed temporary pointers for reference-counted smart pointers. */
      8 
      9 #ifndef AlreadyAddRefed_h
     10 #define AlreadyAddRefed_h
     11 
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Attributes.h"
     14 
     15 namespace mozilla {
     16 
     17 struct unused_t;
     18 
     19 }  // namespace mozilla
     20 
     21 /**
     22 * already_AddRefed cooperates with reference counting smart pointers to enable
     23 * you to assign in a pointer _without_ |AddRef|ing it.  You might want to use
     24 * this as a return type from a function that returns an already |AddRef|ed
     25 * pointer.  Or, you might want to use this as a parameter type in a function
     26 * that wants to force a transfer-of-ownership from a RefPtr in the caller (for
     27 * example, if the function expects callers to pass in a newly-created object,
     28 * which the function then takes ownership of).
     29 *
     30 * TODO Move already_AddRefed to namespace mozilla.  This has not yet been done
     31 * because of the sheer number of usages of already_AddRefed.
     32 *
     33 * When should you use already_AddRefed<>?
     34 * * Ensure a consumer takes ownership of a reference
     35 * * Pass ownership without calling AddRef/Release (sometimes required in
     36 *   off-main-thread code)
     37 * * The ref pointer type you're using doesn't support move construction
     38 *
     39 * Otherwise, use std::move(RefPtr/nsCOMPtr/etc).
     40 */
     41 template <class T>
     42 struct
     43 #if !defined(MOZ_CLANG_PLUGIN) && !defined(XGILL_PLUGIN)
     44    [[nodiscard]]
     45 #endif
     46    MOZ_NON_AUTOABLE already_AddRefed {
     47  already_AddRefed() : mRawPtr(nullptr) {}
     48 
     49  // For simplicity, allow returning nullptr from functions returning
     50  // already_AddRefed<T>. Don't permit returning raw T*, though; it's preferred
     51  // to create already_AddRefed<T> from a reference-counting smart pointer.
     52  MOZ_IMPLICIT already_AddRefed(decltype(nullptr)) : mRawPtr(nullptr) {}
     53  explicit already_AddRefed(T* aRawPtr) : mRawPtr(aRawPtr) {}
     54 
     55  // Disallow copy constructor and copy assignment operator: move semantics used
     56  // instead.
     57  already_AddRefed(const already_AddRefed<T>& aOther) = delete;
     58  already_AddRefed<T>& operator=(const already_AddRefed<T>& aOther) = delete;
     59 
     60  // WARNING: sketchiness ahead.
     61  //
     62  // The x86-64 ABI for Unix-like operating systems requires structures to be
     63  // returned via invisible reference if they are non-trivial for the purposes
     64  // of calls according to the C++ ABI[1].  For our consideration here, that
     65  // means that if we have a non-trivial move constructor or destructor,
     66  // already_AddRefed must be returned by invisible reference.  But
     67  // already_AddRefed is small enough and so commonly used that it would be
     68  // beneficial to return it via registers instead.  So we need to figure out
     69  // a way to make the move constructor and the destructor trivial.
     70  //
     71  // Our destructor is normally non-trivial, because it asserts that the
     72  // stored pointer has been taken by somebody else prior to destruction.
     73  // However, since the assert in question is compiled only for DEBUG builds,
     74  // we can make the destructor trivial in non-DEBUG builds by simply defining
     75  // it with `= default`.
     76  //
     77  // We now have to make the move constructor trivial as well.  It is normally
     78  // non-trivial, because the incoming object has its pointer null-ed during
     79  // the move. This null-ing is done to satisfy the assert in the destructor.
     80  // But since that destructor has no assert in non-DEBUG builds, the clearing
     81  // is unnecessary in such builds; all we really need to perform is a copy of
     82  // the pointer from the incoming object.  So we can let the compiler define
     83  // a trivial move constructor for us, and already_AddRefed can now be
     84  // returned in registers rather than needing to allocate a stack slot for
     85  // an invisible reference.
     86  //
     87  // The above considerations apply to Unix-like operating systems only; the
     88  // conditions for the same optimization to apply on x86-64 Windows are much
     89  // more strigent and are basically impossible for already_AddRefed to
     90  // satisfy[2].  But we do get some benefit from this optimization on Windows
     91  // because we removed the nulling of the pointer during the move, so that's
     92  // a codesize win.
     93  //
     94  // [1] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#non-trivial
     95  // [2] https://docs.microsoft.com/en-us/cpp/build/return-values-cpp
     96 
     97  already_AddRefed(already_AddRefed<T>&& aOther)
     98 #ifdef DEBUG
     99      : mRawPtr(aOther.take()){}
    100 #else
    101      = default;
    102 #endif
    103 
    104        already_AddRefed<T> &
    105        operator=(already_AddRefed<T>&& aOther) {
    106    mRawPtr = aOther.take();
    107    return *this;
    108  }
    109 
    110  /**
    111   * This helper is useful in cases like
    112   *
    113   *  already_AddRefed<BaseClass>
    114   *  Foo()
    115   *  {
    116   *    RefPtr<SubClass> x = ...;
    117   *    return x.forget();
    118   *  }
    119   *
    120   * The autoconversion allows one to omit the idiom
    121   *
    122   *    RefPtr<BaseClass> y = x.forget();
    123   *    return y.forget();
    124   *
    125   * Note that nsRefPtr is the XPCOM reference counting smart pointer class.
    126   */
    127  template <typename U>
    128  MOZ_IMPLICIT already_AddRefed(already_AddRefed<U>&& aOther)
    129      : mRawPtr(aOther.take()) {}
    130 
    131  ~already_AddRefed()
    132 #ifdef DEBUG
    133  {
    134    MOZ_ASSERT(!mRawPtr);
    135  }
    136 #else
    137      = default;
    138 #endif
    139 
    140  [[nodiscard]] T* take() {
    141    T* rawPtr = mRawPtr;
    142    mRawPtr = nullptr;
    143    return rawPtr;
    144  }
    145 
    146  /**
    147   * Once this method is called, we no longer hold any reference to the memory,
    148   * thus leaking it.
    149   * It's equivalent to calling take() and discarding the result, but at least
    150   * it clearly conveys the intent.
    151   */
    152  void leak() { mRawPtr = nullptr; }
    153 
    154  /**
    155   * This helper provides a static_cast replacement for already_AddRefed, so
    156   * if you have
    157   *
    158   *   already_AddRefed<Parent> F();
    159   *
    160   * you can write
    161   *
    162   *   already_AddRefed<Child>
    163   *   G()
    164   *   {
    165   *     return F().downcast<Child>();
    166   *   }
    167   */
    168  template <class U>
    169  already_AddRefed<U> downcast() {
    170    U* tmp = static_cast<U*>(mRawPtr);
    171    mRawPtr = nullptr;
    172    return already_AddRefed<U>(tmp);
    173  }
    174 
    175 private:
    176  T* MOZ_OWNING_REF mRawPtr;
    177 };
    178 
    179 #endif  // AlreadyAddRefed_h