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/. */
6
7 #ifndef nsProxyRelease_h__
8 #define nsProxyRelease_h__
9
10 #include "nsIEventTarget.h"
11 #include "nsIThread.h"
12 #include "nsCOMPtr.h"
13 #include "nsAutoPtr.h"
14 #include "MainThreadUtils.h"
15 #include "nsThreadUtils.h"
16 #include "mozilla/Likely.h"
17 #include "mozilla/Move.h"
18 #include "mozilla/TypeTraits.h"
19 #include "mozilla/Unused.h"
20
21 #ifdef XPCOM_GLUE_AVOID_NSPR
22 #error NS_ProxyRelease implementation depends on NSPR.
23 #endif
24
25 namespace detail {
26
27 template<typename T>
28 class ProxyReleaseEvent : public mozilla::Runnable
29 {
30 public:
ProxyReleaseEvent(already_AddRefed<T> aDoomed)31 explicit ProxyReleaseEvent(already_AddRefed<T> aDoomed)
32 : mDoomed(aDoomed.take()) {}
33
Run()34 NS_IMETHOD Run() override
35 {
36 NS_IF_RELEASE(mDoomed);
37 return NS_OK;
38 }
39
40 private:
41 T* MOZ_OWNING_REF mDoomed;
42 };
43
44 template<typename T>
45 void
ProxyRelease(nsIEventTarget * aTarget,already_AddRefed<T> aDoomed,bool aAlwaysProxy)46 ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed, bool aAlwaysProxy)
47 {
48 // Auto-managing release of the pointer.
49 RefPtr<T> doomed = aDoomed;
50 nsresult rv;
51
52 if (!doomed || !aTarget) {
53 return;
54 }
55
56 if (!aAlwaysProxy) {
57 bool onCurrentThread = false;
58 rv = aTarget->IsOnCurrentThread(&onCurrentThread);
59 if (NS_SUCCEEDED(rv) && onCurrentThread) {
60 return;
61 }
62 }
63
64 nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(doomed.forget());
65
66 rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
67 if (NS_FAILED(rv)) {
68 NS_WARNING("failed to post proxy release event, leaking!");
69 // It is better to leak the aDoomed object than risk crashing as
70 // a result of deleting it on the wrong thread.
71 }
72 }
73
74 template<bool nsISupportsBased>
75 struct ProxyReleaseChooser
76 {
77 template<typename T>
ProxyReleaseProxyReleaseChooser78 static void ProxyRelease(nsIEventTarget* aTarget,
79 already_AddRefed<T> aDoomed,
80 bool aAlwaysProxy)
81 {
82 ::detail::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
83 }
84 };
85
86 template<>
87 struct ProxyReleaseChooser<true>
88 {
89 // We need an intermediate step for handling classes with ambiguous
90 // inheritance to nsISupports.
91 template<typename T>
92 static void ProxyRelease(nsIEventTarget* aTarget,
93 already_AddRefed<T> aDoomed,
94 bool aAlwaysProxy)
95 {
96 ProxyReleaseISupports(aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
97 }
98
99 static void ProxyReleaseISupports(nsIEventTarget* aTarget,
100 nsISupports* aDoomed,
101 bool aAlwaysProxy);
102 };
103
104 } // namespace detail
105
106 /**
107 * Ensures that the delete of a smart pointer occurs on the target thread.
108 *
109 * @param aTarget
110 * the target thread where the doomed object should be released.
111 * @param aDoomed
112 * the doomed object; the object to be released on the target thread.
113 * @param aAlwaysProxy
114 * normally, if NS_ProxyRelease is called on the target thread, then the
115 * doomed object will be released directly. However, if this parameter is
116 * true, then an event will always be posted to the target thread for
117 * asynchronous release.
118 */
119 template<class T>
120 inline NS_HIDDEN_(void)
121 NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
122 bool aAlwaysProxy = false)
123 {
124 ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
125 ::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
126 }
127
128 /**
129 * Ensures that the delete of a smart pointer occurs on the main thread.
130 *
131 * @param aDoomed
132 * the doomed object; the object to be released on the main thread.
133 * @param aAlwaysProxy
134 * normally, if NS_ReleaseOnMainThread is called on the main thread,
135 * then the doomed object will be released directly. However, if this
136 * parameter is true, then an event will always be posted to the main
137 * thread for asynchronous release.
138 */
139 template<class T>
140 inline NS_HIDDEN_(void)
141 NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
142 bool aAlwaysProxy = false)
143 {
144 // NS_ProxyRelease treats a null event target as "the current thread". So a
145 // handle on the main thread is only necessary when we're not already on the
146 // main thread or the release must happen asynchronously.
147 nsCOMPtr<nsIThread> mainThread;
148 if (!NS_IsMainThread() || aAlwaysProxy) {
149 nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
150
151 if (NS_FAILED(rv)) {
152 MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
153 mozilla::Unused << aDoomed.take();
154 return;
155 }
156 }
157
158 NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
159 }
160
161 /**
162 * Class to safely handle main-thread-only pointers off the main thread.
163 *
164 * Classes like XPCWrappedJS are main-thread-only, which means that it is
165 * forbidden to call methods on instances of these classes off the main thread.
166 * For various reasons (see bug 771074), this restriction recently began to
167 * apply to AddRef/Release as well.
168 *
169 * This presents a problem for consumers that wish to hold a callback alive
170 * on non-main-thread code. A common example of this is the proxy callback
171 * pattern, where non-main-thread code holds a strong-reference to the callback
172 * object, and dispatches new Runnables (also with a strong reference) to the
173 * main thread in order to execute the callback. This involves several AddRef
174 * and Release calls on the other thread, which is (now) verboten.
175 *
176 * The basic idea of this class is to introduce a layer of indirection.
177 * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
178 * maintains one strong reference to the main-thread-only object. It must be
179 * instantiated on the main thread (so that the AddRef of the underlying object
180 * happens on the main thread), but consumers may subsequently pass references
181 * to the holder anywhere they please. These references are meant to be opaque
182 * when accessed off-main-thread (assertions enforce this).
183 *
184 * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
185 * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
186 * to the above (though it includes various convenience methods). The basic
187 * pattern is as follows.
188 *
189 * // On the main thread:
190 * nsCOMPtr<nsIFooCallback> callback = ...;
191 * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
192 * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
193 * // Pass callbackHandle to structs/classes that might be accessed on other
194 * // threads.
195 *
196 * All structs and classes that might be accessed on other threads should store
197 * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
198 */
199 template<class T>
200 class nsMainThreadPtrHolder final
201 {
202 public:
203 // We can only acquire a pointer on the main thread. We to fail fast for
204 // threading bugs, so by default we assert if our pointer is used or acquired
205 // off-main-thread. But some consumers need to use the same pointer for
206 // multiple classes, some of which are main-thread-only and some of which
207 // aren't. So we allow them to explicitly disable this strict checking.
208 explicit nsMainThreadPtrHolder(T* aPtr, bool aStrict = true)
209 : mRawPtr(nullptr)
210 , mStrict(aStrict)
211 {
212 // We can only AddRef our pointer on the main thread, which means that the
213 // holder must be constructed on the main thread.
214 MOZ_ASSERT(!mStrict || NS_IsMainThread());
215 NS_IF_ADDREF(mRawPtr = aPtr);
216 }
217 explicit nsMainThreadPtrHolder(already_AddRefed<T> aPtr, bool aString = true)
218 : mRawPtr(aPtr.take())
219 , mStrict(aString)
220 {
221 // Since we don't need to AddRef the pointer, this constructor is safe to
222 // call on any thread.
223 }
224
225 private:
226 // We can be released on any thread.
227 ~nsMainThreadPtrHolder()
228 {
229 if (NS_IsMainThread()) {
230 NS_IF_RELEASE(mRawPtr);
231 } else if (mRawPtr) {
232 NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
233 }
234 }
235
236 public:
237 T* get()
238 {
239 // Nobody should be touching the raw pointer off-main-thread.
240 if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
241 NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
242 MOZ_CRASH();
243 }
244 return mRawPtr;
245 }
246
247 bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
248 {
249 return mRawPtr == aOther.mRawPtr;
250 }
251 bool operator!() const
252 {
253 return !mRawPtr;
254 }
255
256 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
257
258 private:
259 // Our wrapped pointer.
260 T* mRawPtr;
261
262 // Whether to strictly enforce thread invariants in this class.
263 bool mStrict;
264
265 // Copy constructor and operator= not implemented. Once constructed, the
266 // holder is immutable.
267 T& operator=(nsMainThreadPtrHolder& aOther);
268 nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
269 };
270
271 template<class T>
272 class nsMainThreadPtrHandle
273 {
274 RefPtr<nsMainThreadPtrHolder<T>> mPtr;
275
276 public:
277 nsMainThreadPtrHandle() : mPtr(nullptr) {}
278 MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
279 explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
280 : mPtr(aHolder)
281 {
282 }
283 explicit nsMainThreadPtrHandle(
284 already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
285 : mPtr(aHolder)
286 {
287 }
288 nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
289 : mPtr(aOther.mPtr)
290 {
291 }
292 nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
293 {
294 mPtr = aOther.mPtr;
295 return *this;
296 }
297 nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
298 {
299 mPtr = aHolder;
300 return *this;
301 }
302
303 // These all call through to nsMainThreadPtrHolder, and thus implicitly
304 // assert that we're on the main thread. Off-main-thread consumers must treat
305 // these handles as opaque.
306 T* get()
307 {
308 if (mPtr) {
309 return mPtr.get()->get();
310 }
311 return nullptr;
312 }
313 const T* get() const
314 {
315 if (mPtr) {
316 return mPtr.get()->get();
317 }
318 return nullptr;
319 }
320
321 operator T*() { return get(); }
322 T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
323
324 // These are safe to call on other threads with appropriate external locking.
325 bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
326 {
327 if (!mPtr || !aOther.mPtr) {
328 return mPtr == aOther.mPtr;
329 }
330 return *mPtr == *aOther.mPtr;
331 }
332 bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
333 {
334 return !operator==(aOther);
335 }
336 bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
337 bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
338 bool operator!() const {
339 return !mPtr || !*mPtr;
340 }
341 };
342
343 namespace mozilla {
344
345 template<typename T>
346 using PtrHolder = nsMainThreadPtrHolder<T>;
347
348 template<typename T>
349 using PtrHandle = nsMainThreadPtrHandle<T>;
350
351 } // namespace mozilla
352
353 #endif
354