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/. */
7 #include "mozilla/AbstractThread.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/DelayedRunnable.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/MozPromise.h" // We initialize the MozPromise logging in this file.
13 #include "mozilla/ProfilerRunnable.h"
14 #include "mozilla/StateWatching.h" // We initialize the StateWatching logging in this file.
15 #include "mozilla/StaticPtr.h"
16 #include "mozilla/TaskDispatcher.h"
17 #include "mozilla/TaskQueue.h"
18 #include "mozilla/Unused.h"
19 #include "nsContentUtils.h"
20 #include "nsIDirectTaskDispatcher.h"
21 #include "nsIThreadInternal.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsThreadManager.h"
24 #include "nsThreadUtils.h"
29 LazyLogModule
gMozPromiseLog("MozPromise");
30 LazyLogModule
gStateWatchingLog("StateWatching");
32 StaticRefPtr
<AbstractThread
> sMainThread
;
33 MOZ_THREAD_LOCAL(AbstractThread
*) AbstractThread::sCurrentThreadTLS
;
35 class XPCOMThreadWrapper final
: public AbstractThread
,
36 public nsIThreadObserver
,
37 public nsIDirectTaskDispatcher
{
39 XPCOMThreadWrapper(nsIThreadInternal
* aThread
, bool aRequireTailDispatch
,
41 : AbstractThread(aRequireTailDispatch
),
43 mDirectTaskDispatcher(do_QueryInterface(aThread
)),
44 mOnThread(aOnThread
) {
45 MOZ_DIAGNOSTIC_ASSERT(mThread
&& mDirectTaskDispatcher
);
46 MOZ_DIAGNOSTIC_ASSERT(!aOnThread
|| IsCurrentThreadIn());
48 MOZ_ASSERT(!sCurrentThreadTLS
.get(),
49 "There can only be a single XPCOMThreadWrapper available on a "
51 // Set the default current thread so that GetCurrent() never returns
53 sCurrentThreadTLS
.set(this);
57 NS_DECL_THREADSAFE_ISUPPORTS
59 nsresult
Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
,
60 DispatchReason aReason
= NormalDispatch
) override
{
61 nsCOMPtr
<nsIRunnable
> r
= aRunnable
;
62 AbstractThread
* currentThread
;
63 if (aReason
!= TailDispatch
&& (currentThread
= GetCurrent()) &&
64 RequiresTailDispatch(currentThread
) &&
65 currentThread
->IsTailDispatcherAvailable()) {
66 return currentThread
->TailDispatcher().AddTask(this, r
.forget());
69 // At a certain point during shutdown, we stop processing events from the
70 // main thread event queue (this happens long after all _other_ XPCOM
71 // threads have been shut down). However, various bits of subsequent
72 // teardown logic (the media shutdown blocker and the final shutdown cycle
73 // collection) can trigger state watching and state mirroring notifications
74 // that result in dispatch to the main thread. This causes shutdown leaks,
75 // because the |Runner| wrapper below creates a guaranteed cycle
76 // (Thread->EventQueue->Runnable->Thread) until the event is processed. So
77 // if we put the event into a queue that will never be processed, we'll wind
80 // We opt to just release the runnable in that case. Ordinarily, this
81 // approach could cause problems for runnables that are only safe to be
82 // released on the target thread (and not the dispatching thread). This is
83 // why XPCOM thread dispatch explicitly leaks the runnable when dispatch
84 // fails, rather than releasing it. But given that this condition only
85 // applies very late in shutdown when only one thread remains operational,
86 // that concern is unlikely to apply.
87 if (gXPCOMMainThreadEventsAreDoomed
) {
88 return NS_ERROR_FAILURE
;
91 RefPtr
<nsIRunnable
> runner
= new Runner(this, r
.forget());
92 return mThread
->Dispatch(runner
.forget(), NS_DISPATCH_NORMAL
);
95 // Prevent a GCC warning about the other overload of Dispatch being hidden.
96 using AbstractThread::Dispatch
;
98 NS_IMETHOD
RegisterShutdownTask(nsITargetShutdownTask
* aTask
) override
{
99 return mThread
->RegisterShutdownTask(aTask
);
102 NS_IMETHOD
UnregisterShutdownTask(nsITargetShutdownTask
* aTask
) override
{
103 return mThread
->UnregisterShutdownTask(aTask
);
106 bool IsCurrentThreadIn() const override
{
107 return mThread
->IsOnCurrentThread();
110 TaskDispatcher
& TailDispatcher() override
{
111 MOZ_ASSERT(IsCurrentThreadIn());
112 MOZ_ASSERT(IsTailDispatcherAvailable());
113 if (!mTailDispatcher
) {
115 std::make_unique
<AutoTaskDispatcher
>(mDirectTaskDispatcher
,
116 /* aIsTailDispatcher = */ true);
117 mThread
->AddObserver(this);
120 return *mTailDispatcher
;
123 bool IsTailDispatcherAvailable() override
{
124 // Our tail dispatching implementation relies on nsIThreadObserver
125 // callbacks. If we're not doing event processing, it won't work.
127 static_cast<nsThread
*>(mThread
.get())->RecursionDepth() > 0;
131 bool MightHaveTailTasks() override
{ return !!mTailDispatcher
; }
133 nsIEventTarget
* AsEventTarget() override
{ return mThread
; }
135 //-----------------------------------------------------------------------------
137 //-----------------------------------------------------------------------------
138 NS_IMETHOD
OnDispatchedEvent() override
{ return NS_OK
; }
140 NS_IMETHOD
AfterProcessNextEvent(nsIThreadInternal
* thread
,
141 bool eventWasProcessed
) override
{
142 // This is the primary case.
143 MaybeFireTailDispatcher();
147 NS_IMETHOD
OnProcessNextEvent(nsIThreadInternal
* thread
,
148 bool mayWait
) override
{
149 // In general, the tail dispatcher is handled at the end of the current in
150 // AfterProcessNextEvent() above. However, if start spinning a nested event
151 // loop, it's generally better to fire the tail dispatcher before the first
152 // nested event, rather than after it. This check handles that case.
153 MaybeFireTailDispatcher();
157 //-----------------------------------------------------------------------------
158 // nsIDirectTaskDispatcher
159 //-----------------------------------------------------------------------------
160 // Forward calls to nsIDirectTaskDispatcher to the underlying nsThread object.
161 // We can't use the generated NS_FORWARD_NSIDIRECTTASKDISPATCHER macro
162 // as already_AddRefed type must be moved.
163 NS_IMETHOD
DispatchDirectTask(already_AddRefed
<nsIRunnable
> aEvent
) override
{
164 return mDirectTaskDispatcher
->DispatchDirectTask(std::move(aEvent
));
166 NS_IMETHOD
DrainDirectTasks() override
{
167 return mDirectTaskDispatcher
->DrainDirectTasks();
169 NS_IMETHOD
HaveDirectTasks(bool* aResult
) override
{
170 return mDirectTaskDispatcher
->HaveDirectTasks(aResult
);
174 const RefPtr
<nsIThreadInternal
> mThread
;
175 const nsCOMPtr
<nsIDirectTaskDispatcher
> mDirectTaskDispatcher
;
176 std::unique_ptr
<AutoTaskDispatcher
> mTailDispatcher
;
177 const bool mOnThread
;
179 ~XPCOMThreadWrapper() {
181 MOZ_DIAGNOSTIC_ASSERT(IsCurrentThreadIn(),
182 "Must be destroyed on the thread it was created");
183 sCurrentThreadTLS
.set(nullptr);
187 void MaybeFireTailDispatcher() {
188 if (mTailDispatcher
) {
189 mTailDispatcher
->DrainDirectTasks();
190 mThread
->RemoveObserver(this);
191 mTailDispatcher
.reset();
195 class Runner
: public Runnable
{
197 explicit Runner(XPCOMThreadWrapper
* aThread
,
198 already_AddRefed
<nsIRunnable
> aRunnable
)
199 : Runnable("XPCOMThreadWrapper::Runner"),
201 mRunnable(aRunnable
) {}
203 NS_IMETHOD
Run() override
{
204 MOZ_ASSERT(mThread
== AbstractThread::GetCurrent());
205 MOZ_ASSERT(mThread
->IsCurrentThreadIn());
206 SerialEventTargetGuard
guard(mThread
);
207 AUTO_PROFILE_FOLLOWING_RUNNABLE(mRunnable
);
208 return mRunnable
->Run();
211 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
212 NS_IMETHOD
GetName(nsACString
& aName
) override
{
213 aName
.AssignLiteral("AbstractThread::Runner");
214 if (nsCOMPtr
<nsINamed
> named
= do_QueryInterface(mRunnable
)) {
216 named
->GetName(name
);
217 if (!name
.IsEmpty()) {
218 aName
.AppendLiteral(" for ");
227 const RefPtr
<XPCOMThreadWrapper
> mThread
;
228 const RefPtr
<nsIRunnable
> mRunnable
;
232 NS_IMPL_ISUPPORTS(XPCOMThreadWrapper
, nsIThreadObserver
,
233 nsIDirectTaskDispatcher
, nsISerialEventTarget
, nsIEventTarget
)
236 AbstractThread::IsOnCurrentThreadInfallible() { return IsCurrentThreadIn(); }
239 AbstractThread::IsOnCurrentThread(bool* aResult
) {
240 *aResult
= IsCurrentThreadIn();
245 AbstractThread::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
246 nsCOMPtr
<nsIRunnable
> event(aEvent
);
247 return Dispatch(event
.forget(), aFlags
);
251 AbstractThread::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
253 return Dispatch(std::move(aEvent
), NormalDispatch
);
257 AbstractThread::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
259 nsCOMPtr
<nsIRunnable
> event
= aEvent
;
260 NS_ENSURE_TRUE(!!aDelayMs
, NS_ERROR_UNEXPECTED
);
262 RefPtr
<DelayedRunnable
> r
=
263 new DelayedRunnable(do_AddRef(this), event
.forget(), aDelayMs
);
264 nsresult rv
= r
->Init();
265 NS_ENSURE_SUCCESS(rv
, rv
);
267 return Dispatch(r
.forget(), NS_DISPATCH_NORMAL
);
270 nsresult
AbstractThread::TailDispatchTasksFor(AbstractThread
* aThread
) {
271 if (MightHaveTailTasks()) {
272 return TailDispatcher().DispatchTasksFor(aThread
);
278 bool AbstractThread::HasTailTasksFor(AbstractThread
* aThread
) {
279 if (!MightHaveTailTasks()) {
282 return TailDispatcher().HasTasksFor(aThread
);
285 bool AbstractThread::RequiresTailDispatch(AbstractThread
* aThread
) const {
287 // We require tail dispatch if both the source and destination
288 // threads support it.
289 return SupportsTailDispatch() && aThread
->SupportsTailDispatch();
292 bool AbstractThread::RequiresTailDispatchFromCurrentThread() const {
293 AbstractThread
* current
= GetCurrent();
294 return current
&& RequiresTailDispatch(current
);
297 AbstractThread
* AbstractThread::MainThread() {
298 MOZ_ASSERT(sMainThread
);
302 void AbstractThread::InitTLS() {
303 if (!sCurrentThreadTLS
.init()) {
308 void AbstractThread::InitMainThread() {
309 MOZ_ASSERT(NS_IsMainThread());
310 MOZ_ASSERT(!sMainThread
);
311 nsCOMPtr
<nsIThreadInternal
> mainThread
=
312 do_QueryInterface(nsThreadManager::get().GetMainThreadWeak());
313 MOZ_DIAGNOSTIC_ASSERT(mainThread
);
315 if (!sCurrentThreadTLS
.init()) {
318 sMainThread
= new XPCOMThreadWrapper(mainThread
.get(),
319 /* aRequireTailDispatch = */ true,
320 true /* onThread */);
323 void AbstractThread::ShutdownMainThread() {
324 MOZ_ASSERT(NS_IsMainThread());
325 sMainThread
= nullptr;
328 void AbstractThread::DispatchStateChange(
329 already_AddRefed
<nsIRunnable
> aRunnable
) {
330 AbstractThread
* currentThread
= GetCurrent();
331 MOZ_DIAGNOSTIC_ASSERT(currentThread
, "An AbstractThread must exist");
332 if (currentThread
->IsTailDispatcherAvailable()) {
333 currentThread
->TailDispatcher().AddStateChangeTask(this,
334 std::move(aRunnable
));
336 // If the tail dispatcher isn't available, we just avoid sending state
339 // This happens, specifically (1) During async shutdown (via the media
340 // shutdown blocker), and (2) During the final shutdown cycle collection.
341 // Both of these trigger changes to various watched and mirrored state.
342 nsCOMPtr
<nsIRunnable
> neverDispatched
= aRunnable
;
347 void AbstractThread::DispatchDirectTask(
348 already_AddRefed
<nsIRunnable
> aRunnable
) {
349 AbstractThread
* currentThread
= GetCurrent();
350 MOZ_DIAGNOSTIC_ASSERT(currentThread
, "An AbstractThread must exist");
351 if (currentThread
->IsTailDispatcherAvailable()) {
352 currentThread
->TailDispatcher().AddDirectTask(std::move(aRunnable
));
354 // If the tail dispatcher isn't available, we post as a regular task.
355 currentThread
->Dispatch(std::move(aRunnable
));
359 } // namespace mozilla