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 */
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.
30 * class C : public SupportsThreadSafeWeakPtr<C>
33 * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(C)
34 * MOZ_DECLARE_REFCOUNTED_TYPENAME(C)
38 * ThreadSafeWeakPtr<C> weak;
40 * RefPtr<C> strong = new C;
44 * // Make a new weak reference to the object from the strong reference.
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);
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/Unused.h"
71 class ThreadSafeWeakPtr
;
73 class SupportsThreadSafeWeakPtr
;
75 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
76 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \
77 static const char* threadSafeWeakReferenceTypeName() { \
78 return "ThreadSafeWeakReference<" #T ">"; \
81 # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T)
86 // A multiple reader, single writer spin-lock.
87 // This lock maintains an atomic counter which is incremented every time the
88 // lock is acquired reading. So long as the counter remains positive, it may be
89 // incremented for reading multiple times. When acquiring the lock for writing,
90 // we must ensure the counter is 0 (no readers), and if so, set it to a negative
91 // value to indicate that no new readers may take the lock.
92 class ReadWriteSpinLock
{
93 // Only need a type large enough to represent the number of simultaneously
95 typedef int32_t CounterType
;
98 // Try to increment the counter for reading, so long as it is positive.
101 CounterType oldCounter
=
102 mCounter
& std::numeric_limits
<CounterType
>::max();
103 CounterType newCounter
= oldCounter
+ 1;
104 if (mCounter
.compareExchange(oldCounter
, newCounter
)) {
110 // Decrement the counter to remove a read lock.
111 void readUnlock() { mCounter
--; }
113 // Spins until a write lock is acquired. This can only occur if there are no
114 // readers or writers. Once it is acquired, the counter is set to a negative
118 if (mCounter
.compareExchange(0,
119 std::numeric_limits
<CounterType
>::min())) {
125 // Reset the counter to 0.
126 void writeUnlock() { mCounter
= 0; }
129 Atomic
<CounterType
> mCounter
;
132 // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr
133 // object. It guards access to that object via a read-write spinlock.
134 template <typename T
>
135 class ThreadSafeWeakReference
136 : public external::AtomicRefCounted
<ThreadSafeWeakReference
<T
>> {
138 typedef T ElementType
;
140 explicit ThreadSafeWeakReference(T
* aPtr
) { mPtr
= aPtr
; }
142 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
143 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); }
152 friend class mozilla::SupportsThreadSafeWeakPtr
<T
>;
153 template <typename U
>
154 friend class mozilla::ThreadSafeWeakPtr
;
156 // Does an unsafe read of the raw weak pointer.
157 T
* get() const { return mPtr
; }
159 // Creates a new RefPtr to the tracked object.
160 // We need to acquire the read lock while we do this, as we need to atomically
161 // both read the pointer and then increment the refcount on it within the
162 // scope of the lock. This guards against the object being destroyed while in
163 // the middle of creating the new RefPtr.
164 already_AddRefed
<T
> getRefPtr() {
166 RefPtr
<T
> result(get());
168 return result
.forget();
171 // Try to detach the weak reference from the tracked object.
172 // We need to acquire the write lock while we do this, to ensure that no
173 // RefPtr is created to this while detaching. Once acquired, it is safe
174 // to check the refcount and verify that this is the last reference to
175 // the tracked object, so the weak reference can be safely detached.
176 void tryDetach(const SupportsThreadSafeWeakPtr
<T
>* aOwner
) {
178 if (aOwner
->hasOneRef()) {
184 ReadWriteSpinLock mLock
;
188 } // namespace detail
190 template <typename T
>
191 class SupportsThreadSafeWeakPtr
: public external::AtomicRefCounted
<T
> {
193 typedef external::AtomicRefCounted
<T
> AtomicRefCounted
;
194 typedef detail::ThreadSafeWeakReference
<T
> ThreadSafeWeakReference
;
197 ~SupportsThreadSafeWeakPtr() {
198 // Clean up the shared weak reference if one exists.
199 if (ThreadSafeWeakReference
* ptr
= mRef
) {
204 void Release() const {
205 // If there is only one remaining reference to the object when trying to
206 // release, then attempt to detach it from its weak reference. New
207 // references could possibly be created to the object while this happens, so
208 // take care to do this atomically inside tryDetach.
209 if (AtomicRefCounted::hasOneRef()) {
210 if (ThreadSafeWeakReference
* ptr
= mRef
) {
211 ptr
->tryDetach(this);
215 // Once possibly detached, it is now safe to continue to decrement the
217 AtomicRefCounted::Release();
221 template <typename U
>
222 friend class ThreadSafeWeakPtr
;
224 // Creates a shared weak reference for the object if one does not exist. Note
225 // that the object may be of an actual derived type U, but the weak reference
226 // is created for the supplied type T of SupportsThreadSafeWeakPtr<T>.
227 already_AddRefed
<ThreadSafeWeakReference
> getThreadSafeWeakReference() {
228 static_assert(std::is_base_of
<SupportsThreadSafeWeakPtr
<T
>, T
>::value
,
229 "T must derive from SupportsThreadSafeWeakPtr<T>");
232 RefPtr
<ThreadSafeWeakReference
> ptr(
233 new ThreadSafeWeakReference(static_cast<T
*>(this)));
234 // Only set the new weak reference if one does not exist (== nullptr).
235 // If there is already a weak reference, just let this superflous weak
236 // reference get destroyed when it goes out of scope.
237 if (mRef
.compareExchange(nullptr, ptr
)) {
238 // If successful, forget the refcount so that the weak reference stays
240 Unused
<< ptr
.forget();
244 // Create a new RefPtr to weak reference.
245 RefPtr
<ThreadSafeWeakReference
> ptr(mRef
);
249 Atomic
<ThreadSafeWeakReference
*> mRef
;
252 // A thread-safe variant of a weak pointer
253 template <typename T
>
254 class ThreadSafeWeakPtr
{
255 // Be careful to use the weak reference type T in the
256 // SupportsThreadSafeWeakPtr<T> definition.
257 typedef typename
T::ThreadSafeWeakReference ThreadSafeWeakReference
;
260 ThreadSafeWeakPtr() = default;
262 ThreadSafeWeakPtr
& operator=(const ThreadSafeWeakPtr
& aOther
) = default;
263 ThreadSafeWeakPtr(const ThreadSafeWeakPtr
& aOther
) = default;
265 ThreadSafeWeakPtr
& operator=(ThreadSafeWeakPtr
&& aOther
) = default;
266 ThreadSafeWeakPtr(ThreadSafeWeakPtr
&& aOther
) = default;
268 ThreadSafeWeakPtr
& operator=(const RefPtr
<T
>& aOther
) {
270 // Get the underlying shared weak reference to the object, creating one if
272 mRef
= aOther
->getThreadSafeWeakReference();
279 explicit ThreadSafeWeakPtr(const RefPtr
<T
>& aOther
) { *this = aOther
; }
281 ThreadSafeWeakPtr
& operator=(decltype(nullptr)) {
286 explicit ThreadSafeWeakPtr(decltype(nullptr)) {}
288 explicit operator bool() const { return !!get(); }
290 bool operator==(const ThreadSafeWeakPtr
& aOther
) const {
291 return get() == aOther
.get();
294 bool operator==(const RefPtr
<T
>& aOther
) const {
295 return get() == aOther
.get();
298 bool operator==(const T
* aOther
) const { return get() == aOther
; }
300 template <typename U
>
301 bool operator!=(const U
& aOther
) const {
302 return !(*this == aOther
);
305 // Convert the weak pointer to a strong RefPtr.
306 explicit operator RefPtr
<T
>() const { return getRefPtr(); }
309 // Gets a new strong reference of the proper type T to the tracked object.
310 already_AddRefed
<T
> getRefPtr() const {
311 static_assert(std::is_base_of
<typename
ThreadSafeWeakReference::ElementType
,
313 "T must derive from ThreadSafeWeakReference::ElementType");
314 return mRef
? mRef
->getRefPtr().template downcast
<T
>() : nullptr;
317 // Get a pointer to the tracked object, downcasting to the proper type T.
318 // Note that this operation is unsafe as it may cause races if downwind
319 // code depends on the value not to change after reading.
321 static_assert(std::is_base_of
<typename
ThreadSafeWeakReference::ElementType
,
323 "T must derive from ThreadSafeWeakReference::ElementType");
324 return mRef
? static_cast<T
*>(mRef
->get()) : nullptr;
327 // A shared weak reference to an object. Note that this may be null so as to
328 // save memory (at the slight cost of an extra null check) if no object is
330 RefPtr
<ThreadSafeWeakReference
> mRef
;
333 } // namespace mozilla
335 template <typename T
>
336 inline already_AddRefed
<T
> do_AddRef(
337 const mozilla::ThreadSafeWeakPtr
<T
>& aObj
) {
342 #endif /* mozilla_ThreadSafeWeakPtr_h */