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