Bug 1716846 [wpt PR 29402] - Update wpt metadata, a=testonly
[gecko.git] / mfbt / ThreadSafeWeakPtr.h
blobc6b09a7ea14972a73883ed33168b74f8940e6c64
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_THREADSAFEWEAKREFERENCE_TYPENAME(ClassName) and
25 * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your
26 * class, where ClassName is the name of your class.
28 * Example usage:
30 * class C : public SupportsThreadSafeWeakPtr<C>
31 * {
32 * public:
33 * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(C)
34 * MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
35 * void doStuff();
36 * };
38 * ThreadSafeWeakPtr<C> weak;
39 * {
40 * RefPtr<C> strong = new C;
41 * if (strong) {
42 * strong->doStuff();
43 * }
44 * // Make a new weak reference to the object from the strong reference.
45 * weak = strong;
46 * }
47 * MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all "
48 * "strong references are released.");
50 * // Convert the weak reference to a strong reference for usage.
51 * RefPtr<C> other(weak);
52 * if (other) {
53 * other->doStuff();
54 * }
57 #ifndef mozilla_ThreadSafeWeakPtr_h
58 #define mozilla_ThreadSafeWeakPtr_h
60 #include "mozilla/Assertions.h"
61 #include "mozilla/RefCounted.h"
62 #include "mozilla/RefPtr.h"
64 namespace mozilla {
66 template <typename T>
67 class ThreadSafeWeakPtr;
68 template <typename T>
69 class SupportsThreadSafeWeakPtr;
71 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
72 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \
73 static const char* threadSafeWeakReferenceTypeName() { \
74 return "ThreadSafeWeakReference<" #T ">"; \
76 #else
77 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T)
78 #endif
80 namespace detail {
82 // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr
83 // object. This object owns the reference count for the tracked object, and can
84 // perform atomic refcount upgrades.
85 template <typename T>
86 class ThreadSafeWeakReference
87 : public external::AtomicRefCounted<ThreadSafeWeakReference<T>> {
88 public:
89 typedef T ElementType;
91 explicit ThreadSafeWeakReference(T* aPtr) : mPtr(aPtr) {}
93 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
94 const char* typeName() const {
95 // The first time this is called mPtr is null, so don't
96 // invoke any methods on mPtr.
97 return T::threadSafeWeakReferenceTypeName();
99 size_t typeSize() const { return sizeof(*this); }
100 #endif
102 private:
103 friend class mozilla::SupportsThreadSafeWeakPtr<T>;
104 template <typename U>
105 friend class mozilla::ThreadSafeWeakPtr;
107 // Creates a new RefPtr to the tracked object.
108 already_AddRefed<T> getRefPtr() {
109 // Increment our strong reference count only if it is nonzero, meaning that
110 // the object is still alive.
111 MozRefCountType cnt = mStrongCnt.IncrementIfNonzero();
112 if (cnt == 0) {
113 return nullptr;
116 RefPtr<T> result{already_AddRefed(mPtr)};
117 detail::RefCountLogger::logAddRef(result.get(), cnt);
118 return result.forget();
121 // Number of strong references to the underlying data structure.
123 // Other than the initial strong `AddRef` call incrementing this value to 1,
124 // which must occur before any weak references are taken, once this value
125 // reaches `0` again it cannot be changed.
126 RC<MozRefCountType, AtomicRefCount> mStrongCnt{0};
128 // Raw pointer to the tracked object. It is never valid to read this value
129 // outside of `getRefPtr()`.
130 T* MOZ_NON_OWNING_REF mPtr;
133 } // namespace detail
135 // For usage documentation for SupportsThreadSafeWeakPtr, see the header-level
136 // documentation.
138 // To understand the layout of SupportsThreadSafeWeakPtr, consider the following
139 // simplified declaration:
141 // class MyType: SupportsThreadSafeWeakPtr { uint32_t mMyData; ... }
143 // Which will result in the following layout:
145 // +--------------------+
146 // | MyType | <========================================+
147 // +--------------------+ I
148 // | RefPtr mWeakRef o===========> +-------------------------+ I
149 // | uint32_t mMyData | | ThreadSafeWeakReference | I
150 // +--------------------+ +-------------------------+ I
151 // | RC mRefCount | I
152 // | RC mStrongCount | I
153 // | MyType* mPtr o======+
154 // +-------------------------+
156 // The mRefCount inherited from AtomicRefCounted<ThreadSafeWeakReference<T>> is
157 // the weak count. This means MyType implicitly holds a weak reference, so if
158 // the weak count ever hits 0, we know all strong *and* weak references are
159 // gone, and it's safe to free the ThreadSafeWeakReference. MyType's AddRef and
160 // Release implementations otherwise only manipulate mStrongCount.
162 // It's necessary to keep the counts in a separate allocation because we need
163 // to be able to delete MyType while weak references still exist. This ensures
164 // that weak references can still access all the state necessary to check if
165 // they can be upgraded (mStrongCount).
166 template <typename T>
167 class SupportsThreadSafeWeakPtr {
168 protected:
169 typedef detail::ThreadSafeWeakReference<T> ThreadSafeWeakReference;
171 // The `this` pointer will not have subclasses initialized yet, but it will
172 // also not be read until a weak pointer is upgraded, which should be after
173 // this point.
174 SupportsThreadSafeWeakPtr()
175 : mWeakRef(new ThreadSafeWeakReference(static_cast<T*>(this))) {
176 static_assert(std::is_base_of_v<SupportsThreadSafeWeakPtr<T>, T>,
177 "T must derive from SupportsThreadSafeWeakPtr<T>");
180 public:
181 // Compatibility with RefPtr
182 void AddRef() const {
183 auto& refCnt = mWeakRef->mStrongCnt;
184 MOZ_ASSERT(int32_t(refCnt) >= 0);
185 MozRefCountType cnt = ++refCnt;
186 detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
189 void Release() const {
190 auto& refCnt = mWeakRef->mStrongCnt;
191 MOZ_ASSERT(int32_t(refCnt) > 0);
192 detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
193 MozRefCountType cnt = --refCnt;
194 logger.logRelease(cnt);
195 if (0 == cnt) {
196 // Because we have atomically decremented the refcount above, only one
197 // thread can get a 0 count here. Thus, it is safe to access and destroy
198 // |this| here.
199 // No other thread can acquire a strong reference to |this| anymore
200 // through our weak pointer, as upgrading a weak pointer always uses
201 // |IncrementIfNonzero|, meaning the refcount can't leave a zero reference
202 // state.
203 // NOTE: We can't update our refcount to the marker `DEAD` value here, as
204 // it may still be read by mWeakRef.
205 delete static_cast<const T*>(this);
209 // Compatibility with wtf::RefPtr
210 void ref() { AddRef(); }
211 void deref() { Release(); }
212 MozRefCountType refCount() const { return mWeakRef->mStrongCnt; }
214 private:
215 template <typename U>
216 friend class ThreadSafeWeakPtr;
218 ThreadSafeWeakReference* getThreadSafeWeakReference() const {
219 return mWeakRef;
222 const RefPtr<ThreadSafeWeakReference> mWeakRef;
225 // A thread-safe variant of a weak pointer
226 template <typename T>
227 class ThreadSafeWeakPtr {
228 // Be careful to use the weak reference type T in the
229 // SupportsThreadSafeWeakPtr<T> definition.
230 typedef typename T::ThreadSafeWeakReference ThreadSafeWeakReference;
232 public:
233 ThreadSafeWeakPtr() = default;
235 ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default;
236 ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default;
238 ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default;
239 ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default;
241 ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther) {
242 if (aOther) {
243 // Get the underlying shared weak reference to the object.
244 mRef = aOther->getThreadSafeWeakReference();
245 } else {
246 mRef = nullptr;
248 return *this;
251 explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther) { *this = aOther; }
253 ThreadSafeWeakPtr& operator=(decltype(nullptr)) {
254 mRef = nullptr;
255 return *this;
258 explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
260 // Use the explicit `IsNull()` or `IsDead()` methods instead.
261 explicit operator bool() const = delete;
263 // Check if the ThreadSafeWeakPtr was created wrapping a null pointer.
264 bool IsNull() const { return !mRef; }
266 // Check if the managed object is nullptr or has already been destroyed. Once
267 // IsDead returns true, this ThreadSafeWeakPtr can never be upgraded again
268 // (until it has been re-assigned), but a false return value does NOT imply
269 // that any future upgrade will be successful.
270 bool IsDead() const { return IsNull() || size_t(mRef->mStrongCnt) == 0; }
272 bool operator==(const ThreadSafeWeakPtr& aOther) const {
273 return mRef == aOther.mRef;
276 bool operator==(const RefPtr<T>& aOther) const {
277 return *this == aOther.get();
280 bool operator==(const T* aOther) const {
281 if (!mRef) {
282 return !aOther;
284 return aOther && aOther->getThreadSafeWeakReference() == mRef;
287 template <typename U>
288 bool operator!=(const U& aOther) const {
289 return !(*this == aOther);
292 // Convert the weak pointer to a strong RefPtr.
293 explicit operator RefPtr<T>() const { return getRefPtr(); }
295 private:
296 // Gets a new strong reference of the proper type T to the tracked object.
297 already_AddRefed<T> getRefPtr() const {
298 static_assert(std::is_base_of<typename ThreadSafeWeakReference::ElementType,
299 T>::value,
300 "T must derive from ThreadSafeWeakReference::ElementType");
301 return mRef ? mRef->getRefPtr().template downcast<T>() : nullptr;
304 // A shared weak reference to an object. Note that this may be null so as to
305 // save memory (at the slight cost of an extra null check) if no object is
306 // being tracked.
307 RefPtr<ThreadSafeWeakReference> mRef;
310 } // namespace mozilla
312 template <typename T>
313 inline already_AddRefed<T> do_AddRef(
314 const mozilla::ThreadSafeWeakPtr<T>& aObj) {
315 RefPtr<T> ref(aObj);
316 return ref.forget();
319 #endif /* mozilla_ThreadSafeWeakPtr_h */