Backed out changeset b4a0f8afc02e (bug 1857946) for causing bc failures at browser...
[gecko.git] / mfbt / ThreadSafeWeakPtr.h
blobd5176f5ffa2a81768a19fbab2eda1eef8d189740
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/. */
7 /* A thread-safe weak pointer */
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.
14 * Note that SupportsThreadSafeWeakPtr defines the same member functions as
15 * AtomicRefCounted, so you should not separately inherit from it.
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.
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.
27 * Example usage:
29 * class C : public SupportsThreadSafeWeakPtr<C>
30 * {
31 * public:
32 * MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
33 * void doStuff();
34 * };
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.");
48 * // Convert the weak reference to a strong reference for usage.
49 * RefPtr<C> other(weak);
50 * if (other) {
51 * other->doStuff();
52 * }
55 #ifndef mozilla_ThreadSafeWeakPtr_h
56 #define mozilla_ThreadSafeWeakPtr_h
58 #include "mozilla/Assertions.h"
59 #include "mozilla/RefCountType.h"
60 #include "mozilla/RefCounted.h"
61 #include "mozilla/RefPtr.h"
63 namespace mozilla {
65 template <typename T>
66 class ThreadSafeWeakPtr;
68 template <typename T>
69 class SupportsThreadSafeWeakPtr;
71 namespace detail {
73 class SupportsThreadSafeWeakPtrBase {};
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) {}
84 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
85 const char* typeName() const { return "ThreadSafeWeakReference"; }
86 size_t typeSize() const { return sizeof(*this); }
87 #endif
89 private:
90 template <typename U>
91 friend class mozilla::SupportsThreadSafeWeakPtr;
92 template <typename U>
93 friend class mozilla::ThreadSafeWeakPtr;
95 // Number of strong references to the underlying data structure.
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};
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;
107 } // namespace detail
109 // For usage documentation for SupportsThreadSafeWeakPtr, see the header-level
110 // documentation.
112 // To understand the layout of SupportsThreadSafeWeakPtr, consider the following
113 // simplified declaration:
115 // class MyType: SupportsThreadSafeWeakPtr { uint32_t mMyData; ... }
117 // Which will result in the following layout:
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 // +-------------------------------------+
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.
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;
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");
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;
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);
181 return cnt;
184 using HasThreadSafeRefCnt = std::true_type;
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; }
192 private:
193 template <typename U>
194 friend class ThreadSafeWeakPtr;
196 ThreadSafeWeakReference* getThreadSafeWeakReference() const {
197 return mWeakRef;
200 const RefPtr<ThreadSafeWeakReference> mWeakRef;
203 // A thread-safe variant of a weak pointer
204 template <typename T>
205 class ThreadSafeWeakPtr {
206 using ThreadSafeWeakReference = detail::ThreadSafeWeakReference;
208 public:
209 ThreadSafeWeakPtr() = default;
211 ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default;
212 ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default;
214 ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default;
215 ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default;
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;
224 return *this;
227 explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther) { *this = aOther; }
229 ThreadSafeWeakPtr& operator=(decltype(nullptr)) {
230 mRef = nullptr;
231 return *this;
234 explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
236 // Use the explicit `IsNull()` or `IsDead()` methods instead.
237 explicit operator bool() const = delete;
239 // Check if the ThreadSafeWeakPtr was created wrapping a null pointer.
240 bool IsNull() const { return !mRef; }
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; }
248 bool operator==(const ThreadSafeWeakPtr& aOther) const {
249 return mRef == aOther.mRef;
252 bool operator==(const RefPtr<T>& aOther) const {
253 return *this == aOther.get();
256 friend bool operator==(const RefPtr<T>& aStrong,
257 const ThreadSafeWeakPtr& aWeak) {
258 return aWeak == aStrong.get();
261 bool operator==(const T* aOther) const {
262 if (!mRef) {
263 return !aOther;
265 return aOther && aOther->getThreadSafeWeakReference() == mRef;
268 template <typename U>
269 bool operator!=(const U& aOther) const {
270 return !(*this == aOther);
273 // Convert the weak pointer to a strong RefPtr.
274 explicit operator RefPtr<T>() const { return getRefPtr(); }
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;
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;
289 RefPtr<T> ptr = already_AddRefed<T>(static_cast<T*>(mRef->mPtr));
290 detail::RefCountLogger::logAddRef(ptr.get(), cnt);
291 return ptr.forget();
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;
300 } // namespace mozilla
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();
309 #endif /* mozilla_ThreadSafeWeakPtr_h */