Backed out changeset b09d48d2b473 (bug 1655101) for causing mochitest webgl failures...
[gecko.git] / mfbt / WeakPtr.h
blobcb8bdf28e2eb09eafacaf59a4d0120a622b0bb7c
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 /* Weak pointer functionality, implemented as a mixin for use with any class. */
9 /**
10 * SupportsWeakPtr lets you have a pointer to an object 'Foo' without affecting
11 * its lifetime. It works by creating a single shared reference counted object
12 * (WeakReference) that each WeakPtr will access 'Foo' through. This lets 'Foo'
13 * clear the pointer in the WeakReference without having to know about all of
14 * the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
15 * of 'Foo'.
17 * PLEASE NOTE: This weak pointer implementation is not thread-safe.
19 * The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
20 * dereference, and an additional heap allocated pointer sized object shared
21 * between all of the WeakPtrs.
23 * Example of usage:
25 * // To have a class C support weak pointers, inherit from
26 * // SupportsWeakPtr
27 * class C : public SupportsWeakPtr
28 * {
29 * public:
30 * int mNum;
31 * void act();
32 * };
34 * C* ptr = new C();
36 * // Get weak pointers to ptr. The first time a weak pointer
37 * // is obtained, a reference counted WeakReference object is created that
38 * // can live beyond the lifetime of 'ptr'. The WeakReference
39 * // object will be notified of 'ptr's destruction.
40 * WeakPtr<C> weak = ptr;
41 * WeakPtr<C> other = ptr;
43 * // Test a weak pointer for validity before using it.
44 * if (weak) {
45 * weak->mNum = 17;
46 * weak->act();
47 * }
49 * // Destroying the underlying object clears weak pointers to it.
50 * delete ptr;
52 * MOZ_ASSERT(!weak, "Deleting |ptr| clears weak pointers to it.");
53 * MOZ_ASSERT(!other, "Deleting |ptr| clears all weak pointers to it.");
55 * WeakPtr is typesafe and may be used with any class. It is not required that
56 * the class be reference-counted or allocated in any particular way.
58 * The API was loosely inspired by Chromium's weak_ptr.h:
59 * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h
61 * Note that multiple base classes inheriting from SupportsWeakPtr is not
62 * currently supported. We could support it if needed though.
64 * For Gecko-internal usage there is also MainThreadWeakPtr<T>, a version of
65 * WeakPtr that can be destroyed on any thread, but whose release gets proxied
66 * to the main thread. This is a similar API to nsMainThreadPtrHandle, but
67 * without keeping a strong reference to the main-thread object. Said WeakPtr
68 * can't be accessed from any other thread that isn't the main thread.
71 #ifndef mozilla_WeakPtr_h
72 #define mozilla_WeakPtr_h
74 #include "mozilla/ArrayUtils.h"
75 #include "mozilla/Assertions.h"
76 #include "mozilla/Attributes.h"
77 #include "mozilla/Maybe.h"
78 #include "mozilla/RefCounted.h"
79 #include "mozilla/RefPtr.h"
81 #include <string.h>
82 #include <type_traits>
84 #if defined(MOZILLA_INTERNAL_API)
85 // For thread safety checking.
86 # include "nsISupportsImpl.h"
87 // For main thread destructor behavior.
88 # include "nsProxyRelease.h"
89 #endif
91 #if defined(MOZILLA_INTERNAL_API) && \
92 defined(MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED)
94 // Weak referencing is not implemented as thread safe. When a WeakPtr
95 // is created or dereferenced on thread A but the real object is just
96 // being Released() on thread B, there is a possibility of a race
97 // when the proxy object (detail::WeakReference) is notified about
98 // the real object destruction just between when thread A is storing
99 // the object pointer locally and is about to add a reference to it.
101 // Hence, a non-null weak proxy object is considered to have a single
102 // "owning thread". It means that each query for a weak reference,
103 // its dereference, and destruction of the real object must all happen
104 // on a single thread. The following macros implement assertions for
105 // checking these conditions.
107 // We re-use XPCOM's nsAutoOwningEventTarget checks when they are available.
108 // This has the advantage that it works with cooperative thread pools.
110 # define MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK \
111 /* Will be none if mPtr = nullptr. */ \
112 Maybe<nsAutoOwningEventTarget> _owningThread;
113 # define MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK() \
114 do { \
115 if (p) { \
116 _owningThread.emplace(); \
118 } while (false)
119 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY() \
120 do { \
121 MOZ_DIAGNOSTIC_ASSERT( \
122 !_owningThread || _owningThread->IsCurrentThread(), \
123 "WeakPtr accessed from multiple threads"); \
124 } while (false)
125 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(that) \
126 (that)->AssertThreadSafety();
127 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED_IF(that) \
128 do { \
129 if (that) { \
130 (that)->AssertThreadSafety(); \
132 } while (false)
134 # define MOZ_WEAKPTR_THREAD_SAFETY_CHECKING 1
136 #else
138 # define MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK
139 # define MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK() \
140 do { \
141 } while (false)
142 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY() \
143 do { \
144 } while (false)
145 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(that) \
146 do { \
147 } while (false)
148 # define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED_IF(that) \
149 do { \
150 } while (false)
152 #endif
154 namespace mozilla {
156 namespace detail {
158 enum class WeakPtrDestructorBehavior {
159 Normal,
160 #ifdef MOZILLA_INTERNAL_API
161 ProxyToMainThread,
162 #endif
165 } // namespace detail
167 template <typename T, detail::WeakPtrDestructorBehavior =
168 detail::WeakPtrDestructorBehavior::Normal>
169 class WeakPtr;
170 class SupportsWeakPtr;
172 namespace detail {
174 // This can live beyond the lifetime of the class derived from
175 // SupportsWeakPtr.
176 class WeakReference : public ::mozilla::RefCounted<WeakReference> {
177 public:
178 explicit WeakReference(const SupportsWeakPtr* p)
179 : mPtr(const_cast<SupportsWeakPtr*>(p)) {
180 MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK();
183 SupportsWeakPtr* get() const {
184 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY();
185 return mPtr;
188 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
189 const char* typeName() const { return "WeakReference"; }
190 size_t typeSize() const { return sizeof(*this); }
191 #endif
193 #ifdef MOZ_WEAKPTR_THREAD_SAFETY_CHECKING
194 void AssertThreadSafety() { MOZ_WEAKPTR_ASSERT_THREAD_SAFETY(); }
195 #endif
197 private:
198 friend class mozilla::SupportsWeakPtr;
200 void detach() {
201 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY();
202 mPtr = nullptr;
205 SupportsWeakPtr* MOZ_NON_OWNING_REF mPtr;
206 MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK
209 } // namespace detail
211 class SupportsWeakPtr {
212 using WeakReference = detail::WeakReference;
214 protected:
215 ~SupportsWeakPtr() { DetachWeakPtr(); }
217 protected:
218 void DetachWeakPtr() {
219 if (mSelfReferencingWeakReference) {
220 mSelfReferencingWeakReference->detach();
224 private:
225 WeakReference* SelfReferencingWeakReference() const {
226 if (!mSelfReferencingWeakReference) {
227 mSelfReferencingWeakReference = new WeakReference(this);
228 } else {
229 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mSelfReferencingWeakReference);
231 return mSelfReferencingWeakReference.get();
234 template <typename U, detail::WeakPtrDestructorBehavior>
235 friend class WeakPtr;
237 mutable RefPtr<WeakReference> mSelfReferencingWeakReference;
240 template <typename T, detail::WeakPtrDestructorBehavior Destruct>
241 class WeakPtr {
242 using WeakReference = detail::WeakReference;
244 public:
245 WeakPtr& operator=(const WeakPtr& aOther) {
246 // We must make sure the reference we have now is safe to be dereferenced
247 // before we throw it away... (this can be called from a ctor)
248 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED_IF(mRef);
249 // ...and make sure the new reference is used on a single thread as well.
250 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(aOther.mRef);
252 mRef = aOther.mRef;
253 return *this;
256 WeakPtr(const WeakPtr& aOther) {
257 // The thread safety check is performed inside of the operator= method.
258 *this = aOther;
261 WeakPtr& operator=(decltype(nullptr)) {
262 // We must make sure the reference we have now is safe to be dereferenced
263 // before we throw it away.
264 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED_IF(mRef);
265 if (!mRef || mRef->get()) {
266 // Ensure that mRef is dereferenceable in the uninitialized state.
267 mRef = new WeakReference(nullptr);
269 return *this;
272 WeakPtr& operator=(const T* aOther) {
273 // We must make sure the reference we have now is safe to be dereferenced
274 // before we throw it away.
275 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED_IF(mRef);
276 if (aOther) {
277 mRef = aOther->SelfReferencingWeakReference();
278 } else if (!mRef || mRef->get()) {
279 // Ensure that mRef is dereferenceable in the uninitialized state.
280 mRef = new WeakReference(nullptr);
282 // The thread safety check happens inside SelfReferencingWeakPtr
283 // or is initialized in the WeakReference constructor.
284 return *this;
287 MOZ_IMPLICIT WeakPtr(T* aOther) {
288 *this = aOther;
289 #ifdef MOZILLA_INTERNAL_API
290 if (Destruct == detail::WeakPtrDestructorBehavior::ProxyToMainThread) {
291 MOZ_ASSERT(NS_IsMainThread(),
292 "MainThreadWeakPtr makes no sense on non-main threads");
294 #endif
297 explicit WeakPtr(const RefPtr<T>& aOther) : WeakPtr(aOther.get()) {}
299 // Ensure that mRef is dereferenceable in the uninitialized state.
300 WeakPtr() : mRef(new WeakReference(nullptr)) {}
302 explicit operator bool() const { return mRef->get(); }
303 T* get() const { return static_cast<T*>(mRef->get()); }
304 operator T*() const { return get(); }
305 T& operator*() const { return *get(); }
306 T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
308 #ifdef MOZILLA_INTERNAL_API
309 ~WeakPtr() {
310 if (Destruct == detail::WeakPtrDestructorBehavior::ProxyToMainThread) {
311 NS_ReleaseOnMainThread("WeakPtr::mRef", mRef.forget());
312 } else {
313 MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mRef);
316 #endif
318 private:
319 friend class SupportsWeakPtr;
321 explicit WeakPtr(const RefPtr<WeakReference>& aOther) : mRef(aOther) {}
323 RefPtr<WeakReference> mRef;
326 #ifdef MOZILLA_INTERNAL_API
328 template <typename T>
329 using MainThreadWeakPtr =
330 WeakPtr<T, detail::WeakPtrDestructorBehavior::ProxyToMainThread>;
332 #endif
334 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR tmp->DetachWeakPtr();
336 #define NS_IMPL_CYCLE_COLLECTION_WEAK_PTR(class_, ...) \
337 NS_IMPL_CYCLE_COLLECTION_CLASS(class_) \
338 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(class_) \
339 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
340 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR \
341 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
342 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(class_) \
343 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
344 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
346 #define NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(class_, super_, ...) \
347 NS_IMPL_CYCLE_COLLECTION_CLASS(class_) \
348 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(class_, super_) \
349 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
350 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR \
351 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(class_, super_) \
353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
354 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
356 } // namespace mozilla
358 #endif /* mozilla_WeakPtr_h */