Bug 1882593 [wpt PR 44836] - Add test for unknown, invalid ancillary chunk which...
[gecko.git] / xpcom / threads / AbstractThread.cpp
blob61289a6789285963b8b10319903da1a0d9abc21f
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"
25 #include <memory>
27 namespace mozilla {
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 {
38 public:
39 XPCOMThreadWrapper(nsIThreadInternal* aThread, bool aRequireTailDispatch,
40 bool aOnThread)
41 : AbstractThread(aRequireTailDispatch),
42 mThread(aThread),
43 mDirectTaskDispatcher(do_QueryInterface(aThread)),
44 mOnThread(aOnThread) {
45 MOZ_DIAGNOSTIC_ASSERT(mThread && mDirectTaskDispatcher);
46 MOZ_DIAGNOSTIC_ASSERT(!aOnThread || IsCurrentThreadIn());
47 if (aOnThread) {
48 MOZ_ASSERT(!sCurrentThreadTLS.get(),
49 "There can only be a single XPCOMThreadWrapper available on a "
50 "thread");
51 // Set the default current thread so that GetCurrent() never returns
52 // nullptr.
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
78 // up with a leak.
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) {
114 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.
126 bool inEventLoop =
127 static_cast<nsThread*>(mThread.get())->RecursionDepth() > 0;
128 return inEventLoop;
131 bool MightHaveTailTasks() override { return !!mTailDispatcher; }
133 nsIEventTarget* AsEventTarget() override { return mThread; }
135 //-----------------------------------------------------------------------------
136 // nsIThreadObserver
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();
144 return NS_OK;
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();
154 return NS_OK;
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);
173 private:
174 const RefPtr<nsIThreadInternal> mThread;
175 const nsCOMPtr<nsIDirectTaskDispatcher> mDirectTaskDispatcher;
176 std::unique_ptr<AutoTaskDispatcher> mTailDispatcher;
177 const bool mOnThread;
179 ~XPCOMThreadWrapper() {
180 if (mOnThread) {
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 {
196 public:
197 explicit Runner(XPCOMThreadWrapper* aThread,
198 already_AddRefed<nsIRunnable> aRunnable)
199 : Runnable("XPCOMThreadWrapper::Runner"),
200 mThread(aThread),
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)) {
215 nsAutoCString name;
216 named->GetName(name);
217 if (!name.IsEmpty()) {
218 aName.AppendLiteral(" for ");
219 aName.Append(name);
222 return NS_OK;
224 #endif
226 private:
227 const RefPtr<XPCOMThreadWrapper> mThread;
228 const RefPtr<nsIRunnable> mRunnable;
232 NS_IMPL_ISUPPORTS(XPCOMThreadWrapper, nsIThreadObserver,
233 nsIDirectTaskDispatcher, nsISerialEventTarget, nsIEventTarget)
235 NS_IMETHODIMP_(bool)
236 AbstractThread::IsOnCurrentThreadInfallible() { return IsCurrentThreadIn(); }
238 NS_IMETHODIMP
239 AbstractThread::IsOnCurrentThread(bool* aResult) {
240 *aResult = IsCurrentThreadIn();
241 return NS_OK;
244 NS_IMETHODIMP
245 AbstractThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) {
246 nsCOMPtr<nsIRunnable> event(aEvent);
247 return Dispatch(event.forget(), aFlags);
250 NS_IMETHODIMP
251 AbstractThread::Dispatch(already_AddRefed<nsIRunnable> aEvent,
252 uint32_t aFlags) {
253 return Dispatch(std::move(aEvent), NormalDispatch);
256 NS_IMETHODIMP
257 AbstractThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent,
258 uint32_t aDelayMs) {
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);
275 return NS_OK;
278 bool AbstractThread::HasTailTasksFor(AbstractThread* aThread) {
279 if (!MightHaveTailTasks()) {
280 return false;
282 return TailDispatcher().HasTasksFor(aThread);
285 bool AbstractThread::RequiresTailDispatch(AbstractThread* aThread) const {
286 MOZ_ASSERT(aThread);
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);
299 return sMainThread;
302 void AbstractThread::InitTLS() {
303 if (!sCurrentThreadTLS.init()) {
304 MOZ_CRASH();
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()) {
316 MOZ_CRASH();
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));
335 } else {
336 // If the tail dispatcher isn't available, we just avoid sending state
337 // updates.
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;
346 /* static */
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));
353 } else {
354 // If the tail dispatcher isn't available, we post as a regular task.
355 currentThread->Dispatch(std::move(aRunnable));
359 } // namespace mozilla