Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsProxyRelease_h__
7 #define nsProxyRelease_h__
9 #include "nsIEventTarget.h"
10 #include "nsIThread.h"
11 #include "nsCOMPtr.h"
12 #include "nsAutoPtr.h"
13 #include "MainThreadUtils.h"
14 #include "mozilla/Likely.h"
16 #ifdef XPCOM_GLUE_AVOID_NSPR
17 #error NS_ProxyRelease implementation depends on NSPR.
18 #endif
20 /**
21 * Ensure that a nsCOMPtr is released on the target thread.
22 *
23 * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
24 */
25 template <class T>
26 inline NS_HIDDEN_(nsresult)
27 NS_ProxyRelease
28 (nsIEventTarget *target, nsCOMPtr<T> &doomed, bool alwaysProxy=false)
29 {
30 T* raw = nullptr;
31 doomed.swap(raw);
32 return NS_ProxyRelease(target, raw, alwaysProxy);
33 }
35 /**
36 * Ensure that a nsRefPtr is released on the target thread.
37 *
38 * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
39 */
40 template <class T>
41 inline NS_HIDDEN_(nsresult)
42 NS_ProxyRelease
43 (nsIEventTarget *target, nsRefPtr<T> &doomed, bool alwaysProxy=false)
44 {
45 T* raw = nullptr;
46 doomed.swap(raw);
47 return NS_ProxyRelease(target, raw, alwaysProxy);
48 }
50 /**
51 * Ensures that the delete of a nsISupports object occurs on the target thread.
52 *
53 * @param target
54 * the target thread where the doomed object should be released.
55 * @param doomed
56 * the doomed object; the object to be released on the target thread.
57 * @param alwaysProxy
58 * normally, if NS_ProxyRelease is called on the target thread, then the
59 * doomed object will released directly. however, if this parameter is
60 * true, then an event will always be posted to the target thread for
61 * asynchronous release.
62 */
63 NS_COM_GLUE nsresult
64 NS_ProxyRelease
65 (nsIEventTarget *target, nsISupports *doomed, bool alwaysProxy=false);
67 /**
68 * Class to safely handle main-thread-only pointers off the main thread.
69 *
70 * Classes like XPCWrappedJS are main-thread-only, which means that it is
71 * forbidden to call methods on instances of these classes off the main thread.
72 * For various reasons (see bug 771074), this restriction recently began to
73 * apply to AddRef/Release as well.
74 *
75 * This presents a problem for consumers that wish to hold a callback alive
76 * on non-main-thread code. A common example of this is the proxy callback
77 * pattern, where non-main-thread code holds a strong-reference to the callback
78 * object, and dispatches new Runnables (also with a strong reference) to the
79 * main thread in order to execute the callback. This involves several AddRef
80 * and Release calls on the other thread, which is (now) verboten.
81 *
82 * The basic idea of this class is to introduce a layer of indirection.
83 * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
84 * maintains one strong reference to the main-thread-only object. It must be
85 * instantiated on the main thread (so that the AddRef of the underlying object
86 * happens on the main thread), but consumers may subsequently pass references
87 * to the holder anywhere they please. These references are meant to be opaque
88 * when accessed off-main-thread (assertions enforce this).
89 *
90 * The semantics of nsRefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
91 * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
92 * to the above (though it includes various convenience methods). The basic
93 * pattern is as follows.
94 *
95 * // On the main thread:
96 * nsCOMPtr<nsIFooCallback> callback = ...;
97 * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
98 * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
99 * // Pass callbackHandle to structs/classes that might be accessed on other
100 * // threads.
101 *
102 * All structs and classes that might be accessed on other threads should store
103 * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
104 */
105 template<class T>
106 class nsMainThreadPtrHolder MOZ_FINAL
107 {
108 public:
109 // We can only acquire a pointer on the main thread. We to fail fast for
110 // threading bugs, so by default we assert if our pointer is used or acquired
111 // off-main-thread. But some consumers need to use the same pointer for
112 // multiple classes, some of which are main-thread-only and some of which
113 // aren't. So we allow them to explicitly disable this strict checking.
114 nsMainThreadPtrHolder(T* ptr, bool strict = true) : mRawPtr(nullptr), mStrict(strict) {
115 // We can only AddRef our pointer on the main thread, which means that the
116 // holder must be constructed on the main thread.
117 MOZ_ASSERT(!mStrict || NS_IsMainThread());
118 NS_IF_ADDREF(mRawPtr = ptr);
119 }
121 // We can be released on any thread.
122 ~nsMainThreadPtrHolder() {
123 if (NS_IsMainThread()) {
124 NS_IF_RELEASE(mRawPtr);
125 } else if (mRawPtr) {
126 nsCOMPtr<nsIThread> mainThread;
127 NS_GetMainThread(getter_AddRefs(mainThread));
128 if (!mainThread) {
129 NS_WARNING("Couldn't get main thread! Leaking pointer.");
130 return;
131 }
132 NS_ProxyRelease(mainThread, mRawPtr);
133 }
134 }
136 T* get() {
137 // Nobody should be touching the raw pointer off-main-thread.
138 if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
139 NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
140 MOZ_CRASH();
141 }
142 return mRawPtr;
143 }
145 bool operator==(const nsMainThreadPtrHolder<T>& aOther) const { return mRawPtr == aOther.mRawPtr; }
147 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
149 private:
150 // Our wrapped pointer.
151 T* mRawPtr;
153 // Whether to strictly enforce thread invariants in this class.
154 bool mStrict;
156 // Copy constructor and operator= not implemented. Once constructed, the
157 // holder is immutable.
158 T& operator=(nsMainThreadPtrHolder& other);
159 nsMainThreadPtrHolder(const nsMainThreadPtrHolder& other);
160 };
162 template<class T>
163 class nsMainThreadPtrHandle
164 {
165 nsRefPtr<nsMainThreadPtrHolder<T> > mPtr;
167 public:
168 nsMainThreadPtrHandle() : mPtr(nullptr) {}
169 nsMainThreadPtrHandle(nsMainThreadPtrHolder<T> *aHolder) : mPtr(aHolder) {}
170 nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther) : mPtr(aOther.mPtr) {}
171 nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther) {
172 mPtr = aOther.mPtr;
173 return *this;
174 }
176 // These all call through to nsMainThreadPtrHolder, and thus implicitly
177 // assert that we're on the main thread. Off-main-thread consumers must treat
178 // these handles as opaque.
179 T* get()
180 {
181 if (mPtr) {
182 return mPtr.get()->get();
183 }
184 return nullptr;
185 }
186 const T* get() const
187 {
188 if (mPtr) {
189 return mPtr.get()->get();
190 }
191 return nullptr;
192 }
194 operator T*() { return get(); }
195 T* operator->() { return get(); }
197 // These are safe to call on other threads with appropriate external locking.
198 bool operator==(const nsMainThreadPtrHandle<T>& aOther) const {
199 if (!mPtr || !aOther.mPtr)
200 return mPtr == aOther.mPtr;
201 return *mPtr == *aOther.mPtr;
202 }
203 bool operator!() { return !mPtr; }
204 };
206 #endif