Bug 1812499 [wpt PR 38184] - Simplify handling of name-to-subdir mapping in canvas...
[gecko.git] / ipc / mscom / MainThreadInvoker.cpp
blob80628002495cad3dbe6f9122d0611b2bd42a3882
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/mscom/MainThreadInvoker.h"
9 #include "MainThreadUtils.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/BackgroundHangMonitor.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/ProfilerThreadState.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/mscom/SpinEvent.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/Unused.h"
18 #include "private/prpriv.h" // For PR_GetThreadID
19 #include <winternl.h> // For NTSTATUS and NTAPI
21 namespace {
23 typedef NTSTATUS(NTAPI* NtTestAlertPtr)(VOID);
25 /**
26 * SyncRunnable implements different code paths depending on whether or not
27 * we are running on a multiprocessor system. In the multiprocessor case, we
28 * leave the thread in a spin loop while waiting for the main thread to execute
29 * our runnable. Since spinning is pointless in the uniprocessor case, we block
30 * on an event that is set by the main thread once it has finished the runnable.
32 class SyncRunnable : public mozilla::Runnable {
33 public:
34 explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
35 : mozilla::Runnable("MainThreadInvoker"), mRunnable(aRunnable) {
36 static const bool gotStatics = InitStatics();
37 MOZ_ASSERT(gotStatics);
38 Unused << gotStatics;
41 ~SyncRunnable() = default;
43 NS_IMETHOD Run() override {
44 if (mHasRun) {
45 // The APC already ran, so we have nothing to do.
46 return NS_OK;
49 // Run the pending APC in the queue.
50 MOZ_ASSERT(sNtTestAlert);
51 sNtTestAlert();
52 return NS_OK;
55 // This is called by MainThreadInvoker::MainThreadAPC.
56 void APCRun() {
57 mHasRun = true;
59 TimeStamp runStart(TimeStamp::Now());
60 mRunnable->Run();
61 TimeStamp runEnd(TimeStamp::Now());
63 mDuration = runEnd - runStart;
65 mEvent.Signal();
68 bool WaitUntilComplete() {
69 return mEvent.Wait(mozilla::mscom::MainThreadInvoker::GetTargetThread());
72 const mozilla::TimeDuration& GetDuration() const { return mDuration; }
74 private:
75 bool mHasRun = false;
76 nsCOMPtr<nsIRunnable> mRunnable;
77 mozilla::mscom::SpinEvent mEvent;
78 mozilla::TimeDuration mDuration;
80 static NtTestAlertPtr sNtTestAlert;
82 static bool InitStatics() {
83 sNtTestAlert = reinterpret_cast<NtTestAlertPtr>(
84 ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtTestAlert"));
85 MOZ_ASSERT(sNtTestAlert);
86 return sNtTestAlert;
90 NtTestAlertPtr SyncRunnable::sNtTestAlert = nullptr;
92 } // anonymous namespace
94 namespace mozilla {
95 namespace mscom {
97 HANDLE MainThreadInvoker::sMainThread = nullptr;
99 /* static */
100 bool MainThreadInvoker::InitStatics() {
101 nsCOMPtr<nsIThread> mainThread;
102 nsresult rv = ::NS_GetMainThread(getter_AddRefs(mainThread));
103 if (NS_FAILED(rv)) {
104 return false;
107 PRThread* mainPrThread = nullptr;
108 rv = mainThread->GetPRThread(&mainPrThread);
109 if (NS_FAILED(rv)) {
110 return false;
113 PRUint32 tid = ::PR_GetThreadID(mainPrThread);
114 sMainThread = ::OpenThread(SYNCHRONIZE | THREAD_SET_CONTEXT, FALSE, tid);
116 return !!sMainThread;
119 MainThreadInvoker::MainThreadInvoker() {
120 static const bool gotStatics = InitStatics();
121 MOZ_ASSERT(gotStatics);
122 Unused << gotStatics;
125 bool MainThreadInvoker::Invoke(already_AddRefed<nsIRunnable>&& aRunnable) {
126 nsCOMPtr<nsIRunnable> runnable(std::move(aRunnable));
127 if (!runnable) {
128 return false;
131 if (NS_IsMainThread()) {
132 runnable->Run();
133 return true;
136 RefPtr<SyncRunnable> syncRunnable = new SyncRunnable(runnable.forget());
138 // The main thread could be either blocked on a condition variable waiting
139 // for a Gecko event, or it could be blocked waiting on a Windows HANDLE in
140 // IPC code (doing a sync message send). In the former case, we wake it by
141 // posting a Gecko runnable to the main thread. In the latter case, we wake
142 // it using an APC. However, the latter case doesn't happen very often now
143 // and APCs aren't otherwise run by the main thread. To ensure the
144 // SyncRunnable is cleaned up, we need both to run consistently.
145 // To do this, we:
146 // 1. Queue an APC which does the actual work.
147 // This ref gets released in MainThreadAPC when it runs.
148 SyncRunnable* syncRunnableRef = syncRunnable.get();
149 NS_ADDREF(syncRunnableRef);
150 if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
151 reinterpret_cast<UINT_PTR>(syncRunnableRef))) {
152 return false;
155 // 2. Post a Gecko runnable (which always runs). If the APC hasn't run, the
156 // Gecko runnable runs it. Otherwise, it does nothing.
157 if (NS_FAILED(SchedulerGroup::Dispatch(TaskCategory::Other,
158 do_AddRef(syncRunnable)))) {
159 return false;
162 bool result = syncRunnable->WaitUntilComplete();
163 mDuration = syncRunnable->GetDuration();
164 return result;
167 /* static */ VOID CALLBACK MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam) {
168 AUTO_PROFILER_THREAD_WAKE;
169 mozilla::BackgroundHangMonitor().NotifyActivity();
170 MOZ_ASSERT(NS_IsMainThread());
171 auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
172 runnable->APCRun();
173 NS_RELEASE(runnable);
176 } // namespace mscom
177 } // namespace mozilla