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