Backed out changeset f85447f6f56d (bug 1891145) for causing mochitest failures @...
[gecko.git] / ipc / mscom / EnsureMTA.cpp
blob49d8446fa2892a13fcfeacfff6128740ca45bf37
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/ProcessRuntime.h"
14 #include "mozilla/mscom/Utils.h"
15 #include "mozilla/SchedulerGroup.h"
16 #include "mozilla/StaticLocalPtr.h"
17 #include "nsThreadManager.h"
18 #include "nsThreadUtils.h"
20 #include "private/pprthred.h"
22 namespace {
24 class EnterMTARunnable : public mozilla::Runnable {
25 public:
26 EnterMTARunnable() : mozilla::Runnable("EnterMTARunnable") {}
27 NS_IMETHOD Run() override {
28 mozilla::DebugOnly<HRESULT> hr =
29 mozilla::mscom::wrapped::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
30 MOZ_ASSERT(SUCCEEDED(hr));
31 return NS_OK;
35 class BackgroundMTAData {
36 public:
37 BackgroundMTAData() {
38 nsCOMPtr<nsIRunnable> runnable = new EnterMTARunnable();
39 mozilla::DebugOnly<nsresult> rv = NS_NewNamedThread(
40 "COM MTA", getter_AddRefs(mThread), runnable.forget());
41 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_NewNamedThread failed");
42 MOZ_ASSERT(NS_SUCCEEDED(rv));
45 ~BackgroundMTAData() {
46 if (mThread) {
47 mThread->Dispatch(
48 NS_NewRunnableFunction("BackgroundMTAData::~BackgroundMTAData",
49 &mozilla::mscom::wrapped::CoUninitialize),
50 NS_DISPATCH_NORMAL);
51 mThread->Shutdown();
55 nsCOMPtr<nsIThread> GetThread() const { return mThread; }
57 private:
58 nsCOMPtr<nsIThread> mThread;
61 } // anonymous namespace
63 namespace mozilla {
64 namespace mscom {
66 EnsureMTA::EnsureMTA() {
67 MOZ_ASSERT(NS_IsMainThread());
69 // It is possible that we're running so early that we might need to start
70 // the thread manager ourselves. We do this here to guarantee that we have
71 // the ability to start the persistent MTA thread at any moment beyond this
72 // point.
73 nsresult rv = nsThreadManager::get().Init();
74 // We intentionally don't check rv unless we need it when
75 // CoIncremementMTAUsage is unavailable.
77 // Calling this function initializes the MTA without needing to explicitly
78 // create a thread and call CoInitializeEx to do it.
79 // We don't retain the cookie because once we've incremented the MTA, we
80 // leave it that way for the lifetime of the process.
81 CO_MTA_USAGE_COOKIE mtaCookie = nullptr;
82 HRESULT hr = wrapped::CoIncrementMTAUsage(&mtaCookie);
83 if (SUCCEEDED(hr)) {
84 if (NS_SUCCEEDED(rv)) {
85 // Start the persistent MTA thread (mostly) asynchronously.
86 Unused << GetPersistentMTAThread();
89 return;
92 // In the fallback case, we simply initialize our persistent MTA thread.
94 // Make sure thread manager init succeeded before trying to initialize the
95 // persistent MTA thread.
96 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
97 if (NS_FAILED(rv)) {
98 return;
101 // Before proceeding any further, pump a runnable through the persistent MTA
102 // thread to ensure that it is up and running and has finished initializing
103 // the multi-threaded apartment.
104 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
105 "EnsureMTA::EnsureMTA()",
106 []() { MOZ_RELEASE_ASSERT(IsCurrentThreadExplicitMTA()); }));
107 SyncDispatchToPersistentThread(runnable);
110 /* static */
111 nsCOMPtr<nsIThread> EnsureMTA::GetPersistentMTAThread() {
112 static StaticLocalAutoPtr<BackgroundMTAData> sMTAData(
113 []() -> BackgroundMTAData* {
114 BackgroundMTAData* bgData = new BackgroundMTAData();
116 auto setClearOnShutdown = [ptr = &sMTAData]() -> void {
117 ClearOnShutdown(ptr, ShutdownPhase::XPCOMShutdownThreads);
120 if (NS_IsMainThread()) {
121 setClearOnShutdown();
122 return bgData;
125 SchedulerGroup::Dispatch(
126 NS_NewRunnableFunction("mscom::EnsureMTA::GetPersistentMTAThread",
127 std::move(setClearOnShutdown)));
129 return bgData;
130 }());
132 MOZ_ASSERT(sMTAData);
134 return sMTAData->GetThread();
137 /* static */
138 void EnsureMTA::SyncDispatchToPersistentThread(nsIRunnable* aRunnable) {
139 nsCOMPtr<nsIThread> thread(GetPersistentMTAThread());
140 MOZ_ASSERT(thread);
141 if (!thread) {
142 return;
145 // Note that, due to APC dispatch, we might reenter this function while we
146 // wait on this event. We therefore need a unique event object for each
147 // entry into this function. If perf becomes an issue then we will want to
148 // maintain an array of events where the Nth event is unique to the Nth
149 // reentry.
150 nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
151 if (!event) {
152 return;
155 HANDLE eventHandle = event.get();
156 auto eventSetter = [&aRunnable, eventHandle]() -> void {
157 aRunnable->Run();
158 ::SetEvent(eventHandle);
161 nsresult rv = thread->Dispatch(
162 NS_NewRunnableFunction("mscom::EnsureMTA::SyncDispatchToPersistentThread",
163 std::move(eventSetter)),
164 NS_DISPATCH_NORMAL);
165 MOZ_ASSERT(NS_SUCCEEDED(rv));
166 if (NS_FAILED(rv)) {
167 return;
170 AUTO_PROFILER_THREAD_SLEEP;
171 DWORD waitResult;
172 while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, FALSE)) ==
173 WAIT_IO_COMPLETION) {
175 MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
179 * While this function currently appears to be redundant, it may become more
180 * sophisticated in the future. For example, we could optionally dispatch to an
181 * MTA context if we wanted to utilize the MTA thread pool.
183 /* static */
184 void EnsureMTA::SyncDispatch(nsCOMPtr<nsIRunnable>&& aRunnable, Option aOpt) {
185 SyncDispatchToPersistentThread(aRunnable);
188 } // namespace mscom
189 } // namespace mozilla