Bug 1776761 [wpt PR 34611] - [FedCM] Remove traces of revoke, a=testonly
[gecko.git] / ipc / mscom / EnsureMTA.cpp
blob2258dd9dcd3017ac5dbb05f0bbfb227d741e0a72
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/EnsureMTA.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/mscom/COMWrappers.h"
13 #include "mozilla/mscom/Utils.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/StaticLocalPtr.h"
16 #include "nsThreadManager.h"
17 #include "nsThreadUtils.h"
19 #include "private/pprthred.h"
21 namespace {
23 class EnterMTARunnable : public mozilla::Runnable {
24 public:
25 EnterMTARunnable() : mozilla::Runnable("EnterMTARunnable") {}
26 NS_IMETHOD Run() override {
27 mozilla::DebugOnly<HRESULT> hr =
28 mozilla::mscom::wrapped::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
29 MOZ_ASSERT(SUCCEEDED(hr));
30 return NS_OK;
34 class BackgroundMTAData {
35 public:
36 BackgroundMTAData() {
37 nsCOMPtr<nsIRunnable> runnable = new EnterMTARunnable();
38 mozilla::DebugOnly<nsresult> rv = NS_NewNamedThread(
39 "COM MTA", getter_AddRefs(mThread), runnable.forget());
40 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_NewNamedThread failed");
41 MOZ_ASSERT(NS_SUCCEEDED(rv));
44 ~BackgroundMTAData() {
45 if (mThread) {
46 mThread->Dispatch(
47 NS_NewRunnableFunction("BackgroundMTAData::~BackgroundMTAData",
48 &mozilla::mscom::wrapped::CoUninitialize),
49 NS_DISPATCH_NORMAL);
50 mThread->Shutdown();
54 nsCOMPtr<nsIThread> GetThread() const { return mThread; }
56 private:
57 nsCOMPtr<nsIThread> mThread;
60 } // anonymous namespace
62 namespace mozilla {
63 namespace mscom {
65 EnsureMTA::EnsureMTA() {
66 MOZ_ASSERT(NS_IsMainThread());
68 // It is possible that we're running so early that we might need to start
69 // the thread manager ourselves. We do this here to guarantee that we have
70 // the ability to start the persistent MTA thread at any moment beyond this
71 // point.
72 nsresult rv = nsThreadManager::get().Init();
73 // We intentionally don't check rv unless we need it when
74 // CoIncremementMTAUsage is unavailable.
76 // Calling this function initializes the MTA without needing to explicitly
77 // create a thread and call CoInitializeEx to do it.
78 // We don't retain the cookie because once we've incremented the MTA, we
79 // leave it that way for the lifetime of the process.
80 CO_MTA_USAGE_COOKIE mtaCookie = nullptr;
81 HRESULT hr = wrapped::CoIncrementMTAUsage(&mtaCookie);
82 if (SUCCEEDED(hr)) {
83 if (NS_SUCCEEDED(rv)) {
84 // Start the persistent MTA thread (mostly) asynchronously.
85 Unused << GetPersistentMTAThread();
88 return;
91 // In the fallback case, we simply initialize our persistent MTA thread.
93 // Make sure thread manager init succeeded before trying to initialize the
94 // persistent MTA thread.
95 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
96 if (NS_FAILED(rv)) {
97 return;
100 // Before proceeding any further, pump a runnable through the persistent MTA
101 // thread to ensure that it is up and running and has finished initializing
102 // the multi-threaded apartment.
103 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
104 "EnsureMTA::EnsureMTA()",
105 []() { MOZ_RELEASE_ASSERT(IsCurrentThreadExplicitMTA()); }));
106 SyncDispatchToPersistentThread(runnable);
109 /* static */
110 RefPtr<EnsureMTA::CreateInstanceAgileRefPromise>
111 EnsureMTA::CreateInstanceInternal(REFCLSID aClsid, REFIID aIid) {
112 MOZ_ASSERT(IsCurrentThreadExplicitMTA());
114 RefPtr<IUnknown> iface;
115 HRESULT hr = wrapped::CoCreateInstance(aClsid, nullptr, CLSCTX_INPROC_SERVER,
116 aIid, getter_AddRefs(iface));
117 if (FAILED(hr)) {
118 return CreateInstanceAgileRefPromise::CreateAndReject(hr, __func__);
121 // We need to use the two argument constructor for AgileReference because our
122 // RefPtr is not parameterized on the specific interface being requested.
123 AgileReference agileRef(aIid, iface);
124 if (!agileRef) {
125 return CreateInstanceAgileRefPromise::CreateAndReject(agileRef.GetHResult(),
126 __func__);
129 return CreateInstanceAgileRefPromise::CreateAndResolve(std::move(agileRef),
130 __func__);
133 /* static */
134 RefPtr<EnsureMTA::CreateInstanceAgileRefPromise> EnsureMTA::CreateInstance(
135 REFCLSID aClsid, REFIID aIid) {
136 MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
138 const bool isClassOk = IsClassThreadAwareInprocServer(aClsid);
139 MOZ_ASSERT(isClassOk,
140 "mozilla::mscom::EnsureMTA::CreateInstance is not "
141 "safe/performant/necessary to use with this CLSID. This CLSID "
142 "either does not support creation from within a multithreaded "
143 "apartment, or it is not an in-process server.");
144 if (!isClassOk) {
145 return CreateInstanceAgileRefPromise::CreateAndReject(CO_E_NOT_SUPPORTED,
146 __func__);
149 if (IsCurrentThreadExplicitMTA()) {
150 // It's safe to immediately call CreateInstanceInternal
151 return CreateInstanceInternal(aClsid, aIid);
154 // aClsid and aIid are references. Make local copies that we can put into the
155 // lambda in case the sources of aClsid or aIid are not static data
156 CLSID localClsid = aClsid;
157 IID localIid = aIid;
159 auto invoker = [localClsid,
160 localIid]() -> RefPtr<CreateInstanceAgileRefPromise> {
161 return CreateInstanceInternal(localClsid, localIid);
164 nsCOMPtr<nsIThread> mtaThread(GetPersistentMTAThread());
166 return InvokeAsync(mtaThread->SerialEventTarget(), __func__,
167 std::move(invoker));
170 /* static */
171 nsCOMPtr<nsIThread> EnsureMTA::GetPersistentMTAThread() {
172 static StaticLocalAutoPtr<BackgroundMTAData> sMTAData(
173 []() -> BackgroundMTAData* {
174 BackgroundMTAData* bgData = new BackgroundMTAData();
176 auto setClearOnShutdown = [ptr = &sMTAData]() -> void {
177 ClearOnShutdown(ptr, ShutdownPhase::XPCOMShutdownThreads);
180 if (NS_IsMainThread()) {
181 setClearOnShutdown();
182 return bgData;
185 SchedulerGroup::Dispatch(
186 TaskCategory::Other,
187 NS_NewRunnableFunction("mscom::EnsureMTA::GetPersistentMTAThread",
188 std::move(setClearOnShutdown)));
190 return bgData;
191 }());
193 MOZ_ASSERT(sMTAData);
195 return sMTAData->GetThread();
198 /* static */
199 void EnsureMTA::SyncDispatchToPersistentThread(nsIRunnable* aRunnable) {
200 nsCOMPtr<nsIThread> thread(GetPersistentMTAThread());
201 MOZ_ASSERT(thread);
202 if (!thread) {
203 return;
206 // Note that, due to APC dispatch, we might reenter this function while we
207 // wait on this event. We therefore need a unique event object for each
208 // entry into this function. If perf becomes an issue then we will want to
209 // maintain an array of events where the Nth event is unique to the Nth
210 // reentry.
211 nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
212 if (!event) {
213 return;
216 HANDLE eventHandle = event.get();
217 auto eventSetter = [&aRunnable, eventHandle]() -> void {
218 aRunnable->Run();
219 ::SetEvent(eventHandle);
222 nsresult rv = thread->Dispatch(
223 NS_NewRunnableFunction("mscom::EnsureMTA::SyncDispatchToPersistentThread",
224 std::move(eventSetter)),
225 NS_DISPATCH_NORMAL);
226 MOZ_ASSERT(NS_SUCCEEDED(rv));
227 if (NS_FAILED(rv)) {
228 return;
231 #if defined(ACCESSIBILITY)
232 const BOOL alertable = XRE_IsContentProcess() && NS_IsMainThread();
233 #else
234 const BOOL alertable = FALSE;
235 #endif // defined(ACCESSIBILITY)
237 AUTO_PROFILER_THREAD_SLEEP;
238 DWORD waitResult;
239 while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, alertable)) ==
240 WAIT_IO_COMPLETION) {
242 MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
246 * While this function currently appears to be redundant, it may become more
247 * sophisticated in the future. For example, we could optionally dispatch to an
248 * MTA context if we wanted to utilize the MTA thread pool.
250 /* static */
251 void EnsureMTA::SyncDispatch(nsCOMPtr<nsIRunnable>&& aRunnable, Option aOpt) {
252 SyncDispatchToPersistentThread(aRunnable);
255 } // namespace mscom
256 } // namespace mozilla