Bug 1551001 [wpt PR 16740] - Don't mark disconnected tree-scopes for style update...
[gecko.git] / mfbt / ThreadSafeWeakPtr.h
blobe305b2c54ac672c9e1a2827b3dbf679c8de2cbde
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 necessarily already inherits from
15 * AtomicRefCounted, so you should not separately inherit from AtomicRefCounted.
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/Atomics.h"
62 #include "mozilla/RefCounted.h"
63 #include "mozilla/RefPtr.h"
64 #include "mozilla/TypeTraits.h"
65 #include "mozilla/Unused.h"
67 #include <limits>
69 namespace mozilla {
71 template <typename T>
72 class ThreadSafeWeakPtr;
73 template <typename T>
74 class SupportsThreadSafeWeakPtr;
76 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
77 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \
78 static const char* threadSafeWeakReferenceTypeName() { \
79 return "ThreadSafeWeakReference<" #T ">"; \
81 #else
82 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T)
83 #endif
85 namespace detail {
87 // A multiple reader, single writer spin-lock.
88 // This lock maintains an atomic counter which is incremented every time the
89 // lock is acquired reading. So long as the counter remains positive, it may be
90 // incremented for reading multiple times. When acquiring the lock for writing,
91 // we must ensure the counter is 0 (no readers), and if so, set it to a negative
92 // value to indicate that no new readers may take the lock.
93 class ReadWriteSpinLock {
94 // Only need a type large enough to represent the number of simultaneously
95 // accessing threads.
96 typedef int32_t CounterType;
98 public:
99 // Try to increment the counter for reading, so long as it is positive.
100 void readLock() {
101 for (;;) {
102 CounterType oldCounter =
103 mCounter & std::numeric_limits<CounterType>::max();
104 CounterType newCounter = oldCounter + 1;
105 if (mCounter.compareExchange(oldCounter, newCounter)) {
106 break;
111 // Decrement the counter to remove a read lock.
112 void readUnlock() { mCounter--; }
114 // Try to acquire the write lock, but only if there are no readers.
115 // If successful, sets the counter to a negative value.
116 bool tryWriteLock() {
117 return mCounter.compareExchange(0, std::numeric_limits<CounterType>::min());
120 // Reset the counter to 0.
121 void writeUnlock() { mCounter = 0; }
123 private:
124 Atomic<CounterType> mCounter;
127 // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr
128 // object. It guards access to that object via a read-write spinlock.
129 template <typename T>
130 class ThreadSafeWeakReference
131 : public external::AtomicRefCounted<ThreadSafeWeakReference<T>> {
132 public:
133 typedef T ElementType;
135 explicit ThreadSafeWeakReference(T* aPtr) { mPtr = aPtr; }
137 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
138 const char* typeName() const {
139 // The first time this is called mPtr is null, so don't
140 // invoke any methods on mPtr.
141 return T::threadSafeWeakReferenceTypeName();
143 size_t typeSize() const { return sizeof(*this); }
144 #endif
146 private:
147 friend class mozilla::SupportsThreadSafeWeakPtr<T>;
148 template <typename U>
149 friend class mozilla::ThreadSafeWeakPtr;
151 // Does an unsafe read of the raw weak pointer.
152 T* get() const { return mPtr; }
154 // Creates a new RefPtr to the tracked object.
155 // We need to acquire the read lock while we do this, as we need to atomically
156 // both read the pointer and then increment the refcount on it within the
157 // scope of the lock. This guards against the object being destroyed while in
158 // the middle of creating the new RefPtr.
159 already_AddRefed<T> getRefPtr() {
160 mLock.readLock();
161 RefPtr<T> result(get());
162 mLock.readUnlock();
163 return result.forget();
166 // Try to detach the weak reference from the tracked object.
167 // We need to acquire the write lock while we do this, to ensure that no
168 // RefPtr is created to this while detaching. Once acquired, it is safe
169 // to check the refcount and verify that this is the last reference to
170 // the tracked object, so the weak reference can be safely detached.
171 void tryDetach(const SupportsThreadSafeWeakPtr<T>* aOwner) {
172 if (mLock.tryWriteLock()) {
173 if (aOwner->hasOneRef()) {
174 mPtr = nullptr;
176 mLock.writeUnlock();
180 ReadWriteSpinLock mLock;
181 Atomic<T*> mPtr;
184 } // namespace detail
186 template <typename T>
187 class SupportsThreadSafeWeakPtr : public external::AtomicRefCounted<T> {
188 protected:
189 typedef external::AtomicRefCounted<T> AtomicRefCounted;
190 typedef detail::ThreadSafeWeakReference<T> ThreadSafeWeakReference;
192 public:
193 ~SupportsThreadSafeWeakPtr() {
194 // Clean up the shared weak reference if one exists.
195 if (ThreadSafeWeakReference* ptr = mRef) {
196 ptr->Release();
200 void Release() const {
201 // If there is only one remaining reference to the object when trying to
202 // release, then attempt to detach it from its weak reference. New
203 // references could possibly be created to the object while this happens, so
204 // take care to do this atomically inside tryDetach.
205 if (AtomicRefCounted::hasOneRef()) {
206 if (ThreadSafeWeakReference* ptr = mRef) {
207 ptr->tryDetach(this);
211 // Once possibly detached, it is now safe to continue to decrement the
212 // refcount.
213 AtomicRefCounted::Release();
216 private:
217 template <typename U>
218 friend class ThreadSafeWeakPtr;
220 // Creates a shared weak reference for the object if one does not exist. Note
221 // that the object may be of an actual derived type U, but the weak reference
222 // is created for the supplied type T of SupportsThreadSafeWeakPtr<T>.
223 already_AddRefed<ThreadSafeWeakReference> getThreadSafeWeakReference() {
224 static_assert(IsBaseOf<SupportsThreadSafeWeakPtr<T>, T>::value,
225 "T must derive from SupportsThreadSafeWeakPtr<T>");
227 if (!mRef) {
228 RefPtr<ThreadSafeWeakReference> ptr(
229 new ThreadSafeWeakReference(static_cast<T*>(this)));
230 // Only set the new weak reference if one does not exist (== nullptr).
231 // If there is already a weak reference, just let this superflous weak
232 // reference get destroyed when it goes out of scope.
233 if (mRef.compareExchange(nullptr, ptr)) {
234 // If successful, forget the refcount so that the weak reference stays
235 // alive.
236 Unused << ptr.forget();
240 // Create a new RefPtr to weak reference.
241 RefPtr<ThreadSafeWeakReference> ptr(mRef);
242 return ptr.forget();
245 Atomic<ThreadSafeWeakReference*> mRef;
248 // A thread-safe variant of a weak pointer
249 template <typename T>
250 class ThreadSafeWeakPtr {
251 // Be careful to use the weak reference type T in the
252 // SupportsThreadSafeWeakPtr<T> definition.
253 typedef typename T::ThreadSafeWeakReference ThreadSafeWeakReference;
255 public:
256 ThreadSafeWeakPtr() {}
258 ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) {
259 mRef = aOther.mRef;
260 return *this;
263 ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) : mRef(aOther.mRef) {}
265 ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) {
266 mRef = aOther.mRef.forget();
267 return *this;
270 ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) : mRef(aOther.mRef.forget()) {}
272 ThreadSafeWeakPtr& operator=(const RefPtr<T>& aOther) {
273 if (aOther) {
274 // Get the underlying shared weak reference to the object, creating one if
275 // necessary.
276 mRef = aOther->getThreadSafeWeakReference();
277 } else {
278 mRef = nullptr;
280 return *this;
283 explicit ThreadSafeWeakPtr(const RefPtr<T>& aOther) { *this = aOther; }
285 ThreadSafeWeakPtr& operator=(decltype(nullptr)) {
286 mRef = nullptr;
287 return *this;
290 explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
292 explicit operator bool() const { return !!get(); }
294 bool operator==(const ThreadSafeWeakPtr& aOther) const {
295 return get() == aOther.get();
298 bool operator==(const RefPtr<T>& aOther) const {
299 return get() == aOther.get();
302 bool operator==(const T* aOther) const { return get() == aOther; }
304 template <typename U>
305 bool operator!=(const U& aOther) const {
306 return !(*this == aOther);
309 // Convert the weak pointer to a strong RefPtr.
310 explicit operator RefPtr<T>() const { return getRefPtr(); }
312 private:
313 // Gets a new strong reference of the proper type T to the tracked object.
314 already_AddRefed<T> getRefPtr() const {
315 static_assert(
316 IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
317 "T must derive from ThreadSafeWeakReference::ElementType");
318 return mRef ? mRef->getRefPtr().template downcast<T>() : nullptr;
321 // Get a pointer to the tracked object, downcasting to the proper type T.
322 // Note that this operation is unsafe as it may cause races if downwind
323 // code depends on the value not to change after reading.
324 T* get() const {
325 static_assert(
326 IsBaseOf<typename ThreadSafeWeakReference::ElementType, T>::value,
327 "T must derive from ThreadSafeWeakReference::ElementType");
328 return mRef ? static_cast<T*>(mRef->get()) : nullptr;
331 // A shared weak reference to an object. Note that this may be null so as to
332 // save memory (at the slight cost of an extra null check) if no object is
333 // being tracked.
334 RefPtr<ThreadSafeWeakReference> mRef;
337 } // namespace mozilla
339 template <typename T>
340 inline already_AddRefed<T> do_AddRef(
341 const mozilla::ThreadSafeWeakPtr<T>& aObj) {
342 RefPtr<T> ref(aObj);
343 return ref.forget();
346 #endif /* mozilla_ThreadSafeWeakPtr_h */