|
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/. */ |
|
5 |
|
6 #ifndef nsProxyRelease_h__ |
|
7 #define nsProxyRelease_h__ |
|
8 |
|
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" |
|
15 |
|
16 #ifdef XPCOM_GLUE_AVOID_NSPR |
|
17 #error NS_ProxyRelease implementation depends on NSPR. |
|
18 #endif |
|
19 |
|
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 } |
|
34 |
|
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 } |
|
49 |
|
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); |
|
66 |
|
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 } |
|
120 |
|
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 } |
|
135 |
|
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 } |
|
144 |
|
145 bool operator==(const nsMainThreadPtrHolder<T>& aOther) const { return mRawPtr == aOther.mRawPtr; } |
|
146 |
|
147 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>) |
|
148 |
|
149 private: |
|
150 // Our wrapped pointer. |
|
151 T* mRawPtr; |
|
152 |
|
153 // Whether to strictly enforce thread invariants in this class. |
|
154 bool mStrict; |
|
155 |
|
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 }; |
|
161 |
|
162 template<class T> |
|
163 class nsMainThreadPtrHandle |
|
164 { |
|
165 nsRefPtr<nsMainThreadPtrHolder<T> > mPtr; |
|
166 |
|
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 } |
|
175 |
|
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 } |
|
193 |
|
194 operator T*() { return get(); } |
|
195 T* operator->() { return get(); } |
|
196 |
|
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 }; |
|
205 |
|
206 #endif |