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