Bug 1499346 [wpt PR 13540] - Use a common blank reference for wpt/css., a=testonly
[gecko.git] / mfbt / ThreadSafeWeakPtr.h
blob5821315365f849e9f44f1a840ffd5c202da1508b
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 an
11 * atomically refcounted derived class. These thread-safe weak pointers may be safely
12 * accessed and converted to strong pointers on multiple threads.
14 * Note that SupportsThreadSafeWeakPtr necessarily already inherits from AtomicRefCounted,
15 * so you should not separately inherit from AtomicRefCounted.
17 * ThreadSafeWeakPtr and its implementation is distinct from the normal WeakPtr which is
18 * not thread-safe. The interface discipline and implementation details are different enough
19 * that these two implementations are separated for now for efficiency reasons. If you don't
20 * actually need to use weak pointers on multiple threads, you can just use WeakPtr instead.
22 * When deriving from SupportsThreadSafeWeakPtr, you should add
23 * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(ClassName) and
24 * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your class,
25 * where ClassName is the name of your class.
27 * Example usage:
29 * class C : public SupportsThreadSafeWeakPtr<C>
30 * {
31 * public:
32 * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(C)
33 * MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
34 * void doStuff();
35 * };
37 * ThreadSafeWeakPtr<C> weak;
38 * {
39 * RefPtr<C> strong = new C;
40 * if (strong) {
41 * strong->doStuff();
42 * }
43 * // Make a new weak reference to the object from the strong reference.
44 * weak = strong;
45 * }
46 * MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all 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/Atomics.h"
60 #include "mozilla/RefCounted.h"
61 #include "mozilla/RefPtr.h"
62 #include "mozilla/TypeTraits.h"
63 #include "mozilla/Unused.h"
65 #include <limits>
67 namespace mozilla {
69 template<typename T> class ThreadSafeWeakPtr;
70 template<typename T> class SupportsThreadSafeWeakPtr;
72 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
73 #define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \
74 static const char* threadSafeWeakReferenceTypeName() { return "ThreadSafeWeakReference<" #T ">"; }
75 #else
76 #define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T)
77 #endif
79 namespace detail {
81 // A multiple reader, single writer spin-lock.
82 // This lock maintains an atomic counter which is incremented every time the lock is acquired
83 // reading. So long as the counter remains positive, it may be incremented for reading multiple
84 // times. When acquiring the lock for writing, we must ensure the counter is 0 (no readers),
85 // and if so, set it to a negative value to indicate that no new readers may take the lock.
86 class ReadWriteSpinLock
88 // Only need a type large enough to represent the number of simultaneously accessing threads.
89 typedef int32_t CounterType;
91 public:
92 // Try to increment the counter for reading, so long as it is positive.
93 void readLock()
95 for (;;)
97 CounterType oldCounter = mCounter & std::numeric_limits<CounterType>::max();
98 CounterType newCounter = oldCounter + 1;
99 if (mCounter.compareExchange(oldCounter, newCounter)) {
100 break;
105 // Decrement the counter to remove a read lock.
106 void readUnlock()
108 mCounter--;
111 // Try to acquire the write lock, but only if there are no readers.
112 // If successful, sets the counter to a negative value.
113 bool tryWriteLock()
115 return mCounter.compareExchange(0, std::numeric_limits<CounterType>::min());
118 // Reset the counter to 0.
119 void writeUnlock()
121 mCounter = 0;
124 private:
125 Atomic<CounterType> mCounter;
128 // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr object.
129 // It guards access to that object via a read-write spinlock.
130 template<typename T>
131 class ThreadSafeWeakReference : public external::AtomicRefCounted<ThreadSafeWeakReference<T>>
133 public:
134 typedef T ElementType;
136 explicit ThreadSafeWeakReference(T* aPtr)
138 mPtr = aPtr;
141 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
142 const char* typeName() const
144 // The first time this is called mPtr is null, so don't
145 // invoke any methods on mPtr.
146 return T::threadSafeWeakReferenceTypeName();
148 size_t typeSize() const { return sizeof(*this); }
149 #endif
151 private:
152 friend class mozilla::SupportsThreadSafeWeakPtr<T>;
153 template<typename U> friend class mozilla::ThreadSafeWeakPtr;
155 // Does an unsafe read of the raw weak pointer.
156 T* get() const
158 return mPtr;
161 // Creates a new RefPtr to the tracked object.
162 // We need to acquire the read lock while we do this, as we need to atomically
163 // both read the pointer and then increment the refcount on it within the scope
164 // of the lock. This guards against the object being destroyed while in the middle
165 // of creating the new RefPtr.
166 already_AddRefed<T> getRefPtr()
168 mLock.readLock();
169 RefPtr<T> result(get());
170 mLock.readUnlock();
171 return result.forget();
174 // Try to detach the weak reference from the tracked object.
175 // We need to acquire the write lock while we do this, to ensure that no
176 // RefPtr is created to this while detaching. Once acquired, it is safe
177 // to check the refcount and verify that this is the last reference to
178 // the tracked object, so the weak reference can be safely detached.
179 void tryDetach(const SupportsThreadSafeWeakPtr<T>* aOwner)
181 if (mLock.tryWriteLock()) {
182 if (aOwner->hasOneRef()) {
183 mPtr = nullptr;
185 mLock.writeUnlock();
189 ReadWriteSpinLock mLock;
190 Atomic<T*> mPtr;
193 } // namespace detail
195 template<typename T>
196 class SupportsThreadSafeWeakPtr : public external::AtomicRefCounted<T>
198 protected:
199 typedef external::AtomicRefCounted<T> AtomicRefCounted;
200 typedef detail::ThreadSafeWeakReference<T> ThreadSafeWeakReference;
202 public:
203 ~SupportsThreadSafeWeakPtr()
205 // Clean up the shared weak reference if one exists.
206 if (ThreadSafeWeakReference* ptr = mRef) {
207 ptr->Release();
211 void Release() const
213 // If there is only one remaining reference to the object when trying to release,
214 // then attempt to detach it from its weak reference. New references could possibly
215 // be created to the object while this happens, so take care to do this atomically
216 // inside tryDetach.
217 if (AtomicRefCounted::hasOneRef()) {
218 if (ThreadSafeWeakReference* ptr = mRef) {
219 ptr->tryDetach(this);
223 // Once possibly detached, it is now safe to continue to decrement the refcount.
224 AtomicRefCounted::Release();
227 private:
228 template<typename U> friend class ThreadSafeWeakPtr;
230 // Creates a shared weak reference for the object if one does not exist. Note that the
231 // object may be of an actual derived type U, but the weak reference is created for the
232 // supplied type T of SupportsThreadSafeWeakPtr<T>.
233 already_AddRefed<ThreadSafeWeakReference> getThreadSafeWeakReference()
235 static_assert(IsBaseOf<SupportsThreadSafeWeakPtr<T>, T>::value,
236 "T must derive from SupportsThreadSafeWeakPtr<T>");
238 if (!mRef) {
239 RefPtr<ThreadSafeWeakReference> ptr(new ThreadSafeWeakReference(static_cast<T*>(this)));
240 // Only set the new weak reference if one does not exist (== nullptr).
241 // If there is already a weak reference, just let this superflous weak reference get
242 // destroyed when it goes out of scope.
243 if (mRef.compareExchange(nullptr, ptr)) {
244 // If successful, forget the refcount so that the weak reference stays alive.
245 Unused << ptr.forget();
249 // Create a new RefPtr to weak reference.
250 RefPtr<ThreadSafeWeakReference> ptr(mRef);
251 return ptr.forget();
254 Atomic<ThreadSafeWeakReference*> mRef;
257 // A thread-safe variant of a weak pointer
258 template<typename T>
259 class ThreadSafeWeakPtr
261 // Be careful to use the weak reference type T in the SupportsThreadSafeWeakPtr<T> definition.
262 typedef typename T::ThreadSafeWeakReference ThreadSafeWeakReference;
264 public:
265 ThreadSafeWeakPtr()
268 ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther)
270 mRef = aOther.mRef;
271 return *this;
274 ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther)
275 : mRef(aOther.mRef)
279 ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther)
281 mRef = aOther.mRef.forget();
282 return *this;
285 ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther)
286 : mRef(aOther.mRef.forget())
290 ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther)
292 if (aOther) {
293 // Get the underlying shared weak reference to the object, creating one if necessary.
294 mRef = aOther->getThreadSafeWeakReference();
295 } else {
296 mRef = nullptr;
298 return *this;
301 explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther)
303 *this = aOther;
306 ThreadSafeWeakPtr& operator=(decltype(nullptr))
308 mRef = nullptr;
309 return *this;
312 explicit ThreadSafeWeakPtr(decltype(nullptr))
315 explicit operator bool() const
317 return !!get();
320 bool operator==(const ThreadSafeWeakPtr& aOther) const
322 return get() == aOther.get();
325 bool operator==(const RefPtr<T>& aOther) const
327 return get() == aOther.get();
330 bool operator==(const T* aOther) const
332 return get() == aOther;
335 template<typename U>
336 bool operator!=(const U& aOther) const
338 return !(*this == aOther);
341 // Convert the weak pointer to a strong RefPtr.
342 explicit operator RefPtr<T>() const
344 return getRefPtr();
347 private:
348 // Gets a new strong reference of the proper type T to the tracked object.
349 already_AddRefed<T> getRefPtr() const
351 static_assert(IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
352 "T must derive from ThreadSafeWeakReference::ElementType");
353 return mRef ? mRef->getRefPtr().template downcast<T>() : nullptr;
356 // Get a pointer to the tracked object, downcasting to the proper type T.
357 // Note that this operation is unsafe as it may cause races if downwind
358 // code depends on the value not to change after reading.
359 T* get() const
361 static_assert(IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
362 "T must derive from ThreadSafeWeakReference::ElementType");
363 return mRef ? static_cast<T*>(mRef->get()) : nullptr;
366 // A shared weak reference to an object. Note that this may be null so as to save memory
367 // (at the slight cost of an extra null check) if no object is being tracked.
368 RefPtr<ThreadSafeWeakReference> mRef;
371 } // namespace mozilla
373 template<typename T>
374 inline already_AddRefed<T>
375 do_AddRef(const mozilla::ThreadSafeWeakPtr<T>& aObj)
377 RefPtr<T> ref(aObj);
378 return ref.forget();
381 #endif /* mozilla_ThreadSafeWeakPtr_h */