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 XPCOMThreadWrapper : public AbstractThread
32 {
33 public:
XPCOMThreadWrapper(nsIThread * aTarget,bool aRequireTailDispatch)34 explicit XPCOMThreadWrapper(nsIThread* aTarget, bool aRequireTailDispatch)
35 : AbstractThread(aRequireTailDispatch)
36 , 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 MOZ_ASSERT_IF(aRequireTailDispatch,
47 NS_IsMainThread() && NS_GetCurrentThread() == aTarget);
48 }
49
Dispatch(already_AddRefed<nsIRunnable> aRunnable,DispatchFailureHandling aFailureHandling=AssertDispatchSuccess,DispatchReason aReason=NormalDispatch)50 virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
51 DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
52 DispatchReason aReason = NormalDispatch) override
53 {
54 nsCOMPtr<nsIRunnable> r = aRunnable;
55 AbstractThread* currentThread;
56 if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
57 currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
58 return;
59 }
60
61 nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
62 MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
63 Unused << rv;
64 }
65
IsCurrentThreadIn()66 virtual bool IsCurrentThreadIn() override
67 {
68 // Compare NSPR threads so that this works after shutdown when
69 // NS_GetCurrentThread starts returning null.
70 PRThread* thread = nullptr;
71 mTarget->GetPRThread(&thread);
72 bool in = PR_GetCurrentThread() == thread;
73 return in;
74 }
75
FireTailDispatcher()76 void FireTailDispatcher()
77 {
78 MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
79 mTailDispatcher.ref().DrainDirectTasks();
80 mTailDispatcher.reset();
81 }
82
TailDispatcher()83 virtual TaskDispatcher& TailDispatcher() override
84 {
85 MOZ_ASSERT(this == sMainThread); // See the comment in the constructor.
86 MOZ_ASSERT(IsCurrentThreadIn());
87 if (!mTailDispatcher.isSome()) {
88 mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
89
90 nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher);
91 nsContentUtils::RunInStableState(event.forget());
92 }
93
94 return mTailDispatcher.ref();
95 }
96
MightHaveTailTasks()97 virtual bool MightHaveTailTasks() override
98 {
99 return mTailDispatcher.isSome();
100 }
101
AsXPCOMThread()102 virtual nsIThread* AsXPCOMThread() override { return mTarget; }
103
104 private:
105 RefPtr<nsIThread> mTarget;
106 Maybe<AutoTaskDispatcher> mTailDispatcher;
107 };
108
109 void
TailDispatchTasksFor(AbstractThread * aThread)110 AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
111 {
112 if (MightHaveTailTasks()) {
113 TailDispatcher().DispatchTasksFor(aThread);
114 }
115 }
116
117 bool
HasTailTasksFor(AbstractThread * aThread)118 AbstractThread::HasTailTasksFor(AbstractThread* aThread)
119 {
120 if (!MightHaveTailTasks()) {
121 return false;
122 }
123 return TailDispatcher().HasTasksFor(aThread);
124 }
125
126 bool
RequiresTailDispatch(AbstractThread * aThread) const127 AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const
128 {
129 MOZ_ASSERT(aThread);
130 // We require tail dispatch if both the source and destination
131 // threads support it.
132 return SupportsTailDispatch() && aThread->SupportsTailDispatch();
133 }
134
135 bool
RequiresTailDispatchFromCurrentThread() const136 AbstractThread::RequiresTailDispatchFromCurrentThread() const
137 {
138 AbstractThread* current = GetCurrent();
139 return current && RequiresTailDispatch(current);
140 }
141
142 AbstractThread*
MainThread()143 AbstractThread::MainThread()
144 {
145 MOZ_ASSERT(sMainThread);
146 return sMainThread;
147 }
148
149 void
InitStatics()150 AbstractThread::InitStatics()
151 {
152 MOZ_ASSERT(NS_IsMainThread());
153 MOZ_ASSERT(!sMainThread);
154 nsCOMPtr<nsIThread> mainThread;
155 NS_GetMainThread(getter_AddRefs(mainThread));
156 MOZ_DIAGNOSTIC_ASSERT(mainThread);
157 sMainThread = new XPCOMThreadWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
158 ClearOnShutdown(&sMainThread);
159
160 if (!sCurrentThreadTLS.init()) {
161 MOZ_CRASH();
162 }
163 sCurrentThreadTLS.set(sMainThread);
164 }
165
166 void
DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)167 AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)
168 {
169 GetCurrent()->TailDispatcher().AddStateChangeTask(this, Move(aRunnable));
170 }
171
172 /* static */ void
DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)173 AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
174 {
175 GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
176 }
177
178 /* static */
179 already_AddRefed<AbstractThread>
CreateXPCOMThreadWrapper(nsIThread * aThread,bool aRequireTailDispatch)180 AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
181 {
182 RefPtr<XPCOMThreadWrapper> wrapper = new XPCOMThreadWrapper(aThread, aRequireTailDispatch);
183 // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
184 // target thread. This ensures that sCurrentThreadTLS is as expected by
185 // AbstractThread::GetCurrent() on the target thread.
186 nsCOMPtr<nsIRunnable> r =
187 NS_NewRunnableFunction([wrapper]() { sCurrentThreadTLS.set(wrapper); });
188 aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
189 return wrapper.forget();
190 }
191
192 } // namespace mozilla
193