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 */