/src/mozilla-central/xpcom/threads/AbstractThread.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/AbstractThread.h" |
8 | | |
9 | | #include "mozilla/ClearOnShutdown.h" |
10 | | #include "mozilla/Maybe.h" |
11 | | #include "mozilla/MozPromise.h" // We initialize the MozPromise logging in this file. |
12 | | #include "mozilla/StaticPtr.h" |
13 | | #include "mozilla/StateWatching.h" // We initialize the StateWatching logging in this file. |
14 | | #include "mozilla/TaskQueue.h" |
15 | | #include "mozilla/TaskDispatcher.h" |
16 | | #include "mozilla/Unused.h" |
17 | | |
18 | | #include "nsThreadUtils.h" |
19 | | #include "nsContentUtils.h" |
20 | | #include "nsServiceManagerUtils.h" |
21 | | |
22 | | |
23 | | namespace mozilla { |
24 | | |
25 | | LazyLogModule gMozPromiseLog("MozPromise"); |
26 | | LazyLogModule gStateWatchingLog("StateWatching"); |
27 | | |
28 | | StaticRefPtr<AbstractThread> sMainThread; |
29 | | MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS; |
30 | | |
31 | | class EventTargetWrapper : public AbstractThread |
32 | | { |
33 | | public: |
34 | | explicit EventTargetWrapper(nsIEventTarget* aTarget, bool aRequireTailDispatch) |
35 | | : AbstractThread(aRequireTailDispatch) |
36 | | , mTarget(aTarget) |
37 | 3 | { |
38 | 3 | // Our current mechanism of implementing tail dispatch is appshell-specific. |
39 | 3 | // This is because a very similar mechanism already exists on the main |
40 | 3 | // thread, and we want to avoid making event dispatch on the main thread |
41 | 3 | // more complicated than it already is. |
42 | 3 | // |
43 | 3 | // If you need to use tail dispatch on other XPCOM threads, you'll need to |
44 | 3 | // implement an nsIThreadObserver to fire the tail dispatcher at the |
45 | 3 | // appropriate times. You will also need to modify this assertion. |
46 | 3 | MOZ_ASSERT_IF(aRequireTailDispatch, NS_IsMainThread() && aTarget->IsOnCurrentThread()); |
47 | 3 | } |
48 | | |
49 | | virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable, |
50 | | DispatchReason aReason = NormalDispatch) override |
51 | 3 | { |
52 | 3 | AbstractThread* currentThread; |
53 | 3 | if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) { |
54 | 0 | return currentThread->TailDispatcher().AddTask(this, std::move(aRunnable)); |
55 | 0 | } |
56 | 3 | |
57 | 3 | RefPtr<nsIRunnable> runner(new Runner(this, std::move(aRunnable), false /* already drained by TaskGroupRunnable */)); |
58 | 3 | return mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL); |
59 | 3 | } |
60 | | |
61 | | // Prevent a GCC warning about the other overload of Dispatch being hidden. |
62 | | using AbstractThread::Dispatch; |
63 | | |
64 | | virtual bool IsCurrentThreadIn() override |
65 | 0 | { |
66 | 0 | return mTarget->IsOnCurrentThread(); |
67 | 0 | } |
68 | | |
69 | | void FireTailDispatcher() |
70 | 0 | { |
71 | 0 | AutoEnter context(this); |
72 | 0 |
|
73 | 0 | MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome()); |
74 | 0 | mTailDispatcher.ref().DrainDirectTasks(); |
75 | 0 | mTailDispatcher.reset(); |
76 | 0 | } |
77 | | |
78 | | virtual TaskDispatcher& TailDispatcher() override |
79 | 0 | { |
80 | 0 | MOZ_ASSERT(IsCurrentThreadIn()); |
81 | 0 | if (!mTailDispatcher.isSome()) { |
82 | 0 | mTailDispatcher.emplace(/* aIsTailDispatcher = */ true); |
83 | 0 |
|
84 | 0 | nsCOMPtr<nsIRunnable> event = |
85 | 0 | NewRunnableMethod("EventTargetWrapper::FireTailDispatcher", |
86 | 0 | this, |
87 | 0 | &EventTargetWrapper::FireTailDispatcher); |
88 | 0 | nsContentUtils::RunInStableState(event.forget()); |
89 | 0 | } |
90 | 0 |
|
91 | 0 | return mTailDispatcher.ref(); |
92 | 0 | } |
93 | | |
94 | | virtual bool MightHaveTailTasks() override |
95 | 0 | { |
96 | 0 | return mTailDispatcher.isSome(); |
97 | 0 | } |
98 | | |
99 | 0 | virtual nsIEventTarget* AsEventTarget() override { return mTarget; } |
100 | | |
101 | | private: |
102 | | nsCOMPtr<nsIThread> mRunningThread; |
103 | | RefPtr<nsIEventTarget> mTarget; |
104 | | Maybe<AutoTaskDispatcher> mTailDispatcher; |
105 | | |
106 | | virtual already_AddRefed<nsIRunnable> |
107 | | CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable) override |
108 | 0 | { |
109 | 0 | RefPtr<Runner> runner = |
110 | 0 | new Runner(this, std::move(aRunnable), /* aDrainDirectTasks */ true); |
111 | 0 | return runner.forget(); |
112 | 0 | } |
113 | | |
114 | | class Runner : public CancelableRunnable { |
115 | | class MOZ_STACK_CLASS AutoTaskGuard final { |
116 | | public: |
117 | | explicit AutoTaskGuard(EventTargetWrapper* aThread) |
118 | | : mLastCurrentThread(nullptr) |
119 | 0 | { |
120 | 0 | MOZ_ASSERT(aThread); |
121 | 0 | mLastCurrentThread = sCurrentThreadTLS.get(); |
122 | 0 | sCurrentThreadTLS.set(aThread); |
123 | 0 | } |
124 | | |
125 | | ~AutoTaskGuard() |
126 | 0 | { |
127 | 0 | sCurrentThreadTLS.set(mLastCurrentThread); |
128 | 0 | } |
129 | | private: |
130 | | AbstractThread* mLastCurrentThread; |
131 | | }; |
132 | | |
133 | | public: |
134 | | explicit Runner(EventTargetWrapper* aThread, |
135 | | already_AddRefed<nsIRunnable> aRunnable, |
136 | | bool aDrainDirectTasks) |
137 | | : CancelableRunnable("EventTargetWrapper::Runner") |
138 | | , mThread(aThread) |
139 | | , mRunnable(aRunnable) |
140 | | , mDrainDirectTasks(aDrainDirectTasks) |
141 | 3 | { |
142 | 3 | } |
143 | | |
144 | | NS_IMETHOD Run() override |
145 | 0 | { |
146 | 0 | AutoTaskGuard taskGuard(mThread); |
147 | 0 |
|
148 | 0 | MOZ_ASSERT(mThread == AbstractThread::GetCurrent()); |
149 | 0 | MOZ_ASSERT(mThread->IsCurrentThreadIn()); |
150 | 0 | nsresult rv = mRunnable->Run(); |
151 | 0 |
|
152 | 0 | if (mDrainDirectTasks) { |
153 | 0 | mThread->TailDispatcher().DrainDirectTasks(); |
154 | 0 | } |
155 | 0 |
|
156 | 0 | return rv; |
157 | 0 | } |
158 | | |
159 | | nsresult Cancel() override |
160 | 0 | { |
161 | 0 | // Set the TLS during Cancel() just in case it calls Run(). |
162 | 0 | AutoTaskGuard taskGuard(mThread); |
163 | 0 |
|
164 | 0 | nsresult rv = NS_OK; |
165 | 0 |
|
166 | 0 | // Try to cancel the runnable if it implements the right interface. |
167 | 0 | // Otherwise just skip the runnable. |
168 | 0 | nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mRunnable); |
169 | 0 | if (cr) { |
170 | 0 | rv = cr->Cancel(); |
171 | 0 | } |
172 | 0 |
|
173 | 0 | return rv; |
174 | 0 | } |
175 | | |
176 | | #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY |
177 | | NS_IMETHOD GetName(nsACString& aName) override |
178 | 0 | { |
179 | 0 | aName.AssignLiteral("AbstractThread::Runner"); |
180 | 0 | if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) { |
181 | 0 | nsAutoCString name; |
182 | 0 | named->GetName(name); |
183 | 0 | if (!name.IsEmpty()) { |
184 | 0 | aName.AppendLiteral(" for "); |
185 | 0 | aName.Append(name); |
186 | 0 | } |
187 | 0 | } |
188 | 0 | return NS_OK; |
189 | 0 | } |
190 | | #endif |
191 | | |
192 | | private: |
193 | | RefPtr<EventTargetWrapper> mThread; |
194 | | RefPtr<nsIRunnable> mRunnable; |
195 | | bool mDrainDirectTasks; |
196 | | }; |
197 | | }; |
198 | | |
199 | | NS_IMPL_ISUPPORTS(AbstractThread, nsIEventTarget, nsISerialEventTarget) |
200 | | |
201 | | NS_IMETHODIMP_(bool) |
202 | | AbstractThread::IsOnCurrentThreadInfallible() |
203 | 0 | { |
204 | 0 | return IsCurrentThreadIn(); |
205 | 0 | } |
206 | | |
207 | | NS_IMETHODIMP |
208 | | AbstractThread::IsOnCurrentThread(bool* aResult) |
209 | 0 | { |
210 | 0 | *aResult = IsCurrentThreadIn(); |
211 | 0 | return NS_OK; |
212 | 0 | } |
213 | | |
214 | | NS_IMETHODIMP |
215 | | AbstractThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) |
216 | 0 | { |
217 | 0 | nsCOMPtr<nsIRunnable> event(aEvent); |
218 | 0 | return Dispatch(event.forget(), aFlags); |
219 | 0 | } |
220 | | |
221 | | NS_IMETHODIMP |
222 | | AbstractThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) |
223 | 0 | { |
224 | 0 | return Dispatch(std::move(aEvent), NormalDispatch); |
225 | 0 | } |
226 | | |
227 | | NS_IMETHODIMP |
228 | | AbstractThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, |
229 | | uint32_t aDelayMs) |
230 | 0 | { |
231 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
232 | 0 | } |
233 | | |
234 | | nsresult |
235 | | AbstractThread::TailDispatchTasksFor(AbstractThread* aThread) |
236 | 0 | { |
237 | 0 | if (MightHaveTailTasks()) { |
238 | 0 | return TailDispatcher().DispatchTasksFor(aThread); |
239 | 0 | } |
240 | 0 | |
241 | 0 | return NS_OK; |
242 | 0 | } |
243 | | |
244 | | bool |
245 | | AbstractThread::HasTailTasksFor(AbstractThread* aThread) |
246 | 0 | { |
247 | 0 | if (!MightHaveTailTasks()) { |
248 | 0 | return false; |
249 | 0 | } |
250 | 0 | return TailDispatcher().HasTasksFor(aThread); |
251 | 0 | } |
252 | | |
253 | | bool |
254 | | AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const |
255 | 0 | { |
256 | 0 | MOZ_ASSERT(aThread); |
257 | 0 | // We require tail dispatch if both the source and destination |
258 | 0 | // threads support it. |
259 | 0 | return SupportsTailDispatch() && aThread->SupportsTailDispatch(); |
260 | 0 | } |
261 | | |
262 | | bool |
263 | | AbstractThread::RequiresTailDispatchFromCurrentThread() const |
264 | 0 | { |
265 | 0 | AbstractThread* current = GetCurrent(); |
266 | 0 | return current && RequiresTailDispatch(current); |
267 | 0 | } |
268 | | |
269 | | AbstractThread* |
270 | | AbstractThread::MainThread() |
271 | 3 | { |
272 | 3 | MOZ_ASSERT(sMainThread); |
273 | 3 | return sMainThread; |
274 | 3 | } |
275 | | |
276 | | void |
277 | | AbstractThread::InitTLS() |
278 | 3 | { |
279 | 3 | if (!sCurrentThreadTLS.init()) { |
280 | 0 | MOZ_CRASH(); |
281 | 0 | } |
282 | 3 | } |
283 | | |
284 | | void |
285 | | AbstractThread::InitMainThread() |
286 | 3 | { |
287 | 3 | MOZ_ASSERT(NS_IsMainThread()); |
288 | 3 | MOZ_ASSERT(!sMainThread); |
289 | 3 | nsCOMPtr<nsIThread> mainThread; |
290 | 3 | NS_GetMainThread(getter_AddRefs(mainThread)); |
291 | 3 | MOZ_DIAGNOSTIC_ASSERT(mainThread); |
292 | 3 | sMainThread = new EventTargetWrapper(mainThread.get(), /* aRequireTailDispatch = */ true); |
293 | 3 | ClearOnShutdown(&sMainThread); |
294 | 3 | |
295 | 3 | if (!sCurrentThreadTLS.init()) { |
296 | 0 | MOZ_CRASH(); |
297 | 0 | } |
298 | 3 | } |
299 | | |
300 | | void |
301 | | AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable) |
302 | 0 | { |
303 | 0 | GetCurrent()->TailDispatcher().AddStateChangeTask(this, std::move(aRunnable)); |
304 | 0 | } |
305 | | |
306 | | /* static */ void |
307 | | AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable) |
308 | 0 | { |
309 | 0 | GetCurrent()->TailDispatcher().AddDirectTask(std::move(aRunnable)); |
310 | 0 | } |
311 | | |
312 | | /* static */ |
313 | | already_AddRefed<AbstractThread> |
314 | | AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch) |
315 | 0 | { |
316 | 0 | RefPtr<EventTargetWrapper> wrapper = new EventTargetWrapper(aThread, aRequireTailDispatch); |
317 | 0 |
|
318 | 0 | bool onCurrentThread = false; |
319 | 0 | Unused << aThread->IsOnCurrentThread(&onCurrentThread); |
320 | 0 |
|
321 | 0 | if (onCurrentThread) { |
322 | 0 | sCurrentThreadTLS.set(wrapper); |
323 | 0 | return wrapper.forget(); |
324 | 0 | } |
325 | 0 | |
326 | 0 | // Set the thread-local sCurrentThreadTLS to point to the wrapper on the |
327 | 0 | // target thread. This ensures that sCurrentThreadTLS is as expected by |
328 | 0 | // AbstractThread::GetCurrent() on the target thread. |
329 | 0 | nsCOMPtr<nsIRunnable> r = |
330 | 0 | NS_NewRunnableFunction("AbstractThread::CreateXPCOMThreadWrapper", |
331 | 0 | [wrapper]() { sCurrentThreadTLS.set(wrapper); }); |
332 | 0 | aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); |
333 | 0 | return wrapper.forget(); |
334 | 0 | } |
335 | | |
336 | | /* static */ |
337 | | already_AddRefed<AbstractThread> |
338 | | AbstractThread::CreateEventTargetWrapper(nsIEventTarget* aEventTarget, |
339 | | bool aRequireTailDispatch) |
340 | 0 | { |
341 | 0 | MOZ_ASSERT(aEventTarget); |
342 | 0 | nsCOMPtr<nsIThread> thread(do_QueryInterface(aEventTarget)); |
343 | 0 | Unused << thread; // simpler than DebugOnly<nsCOMPtr<nsIThread>> |
344 | 0 | MOZ_ASSERT(!thread, "nsIThread should be wrapped by CreateXPCOMThreadWrapper!"); |
345 | 0 |
|
346 | 0 | RefPtr<EventTargetWrapper> wrapper = |
347 | 0 | new EventTargetWrapper(aEventTarget, aRequireTailDispatch); |
348 | 0 |
|
349 | 0 | return wrapper.forget(); |
350 | 0 | } |
351 | | |
352 | | } // namespace mozilla |