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"
24 class EnterMTARunnable
: public mozilla::Runnable
{
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
));
35 class 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() {
48 NS_NewRunnableFunction("BackgroundMTAData::~BackgroundMTAData",
49 &mozilla::mscom::wrapped::CoUninitialize
),
55 nsCOMPtr
<nsIThread
> GetThread() const { return mThread
; }
58 nsCOMPtr
<nsIThread
> mThread
;
61 } // anonymous namespace
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
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
);
84 if (NS_SUCCEEDED(rv
)) {
85 // Start the persistent MTA thread (mostly) asynchronously.
86 Unused
<< GetPersistentMTAThread();
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
));
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
);
111 RefPtr
<EnsureMTA::CreateInstanceAgileRefPromise
>
112 EnsureMTA::CreateInstanceInternal(REFCLSID aClsid
, REFIID aIid
) {
113 MOZ_ASSERT(IsCurrentThreadExplicitMTA());
115 RefPtr
<IUnknown
> iface
;
116 HRESULT hr
= wrapped::CoCreateInstance(aClsid
, nullptr, CLSCTX_INPROC_SERVER
,
117 aIid
, getter_AddRefs(iface
));
119 return CreateInstanceAgileRefPromise::CreateAndReject(hr
, __func__
);
122 // We need to use the two argument constructor for AgileReference because our
123 // RefPtr is not parameterized on the specific interface being requested.
124 AgileReference
agileRef(aIid
, iface
);
126 return CreateInstanceAgileRefPromise::CreateAndReject(agileRef
.GetHResult(),
130 return CreateInstanceAgileRefPromise::CreateAndResolve(std::move(agileRef
),
135 RefPtr
<EnsureMTA::CreateInstanceAgileRefPromise
> EnsureMTA::CreateInstance(
136 REFCLSID aClsid
, REFIID aIid
) {
137 MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
139 const bool isClassOk
= IsClassThreadAwareInprocServer(aClsid
);
140 MOZ_ASSERT(isClassOk
,
141 "mozilla::mscom::EnsureMTA::CreateInstance is not "
142 "safe/performant/necessary to use with this CLSID. This CLSID "
143 "either does not support creation from within a multithreaded "
144 "apartment, or it is not an in-process server.");
146 return CreateInstanceAgileRefPromise::CreateAndReject(CO_E_NOT_SUPPORTED
,
150 if (IsCurrentThreadExplicitMTA()) {
151 // It's safe to immediately call CreateInstanceInternal
152 return CreateInstanceInternal(aClsid
, aIid
);
155 // aClsid and aIid are references. Make local copies that we can put into the
156 // lambda in case the sources of aClsid or aIid are not static data
157 CLSID localClsid
= aClsid
;
160 auto invoker
= [localClsid
,
161 localIid
]() -> RefPtr
<CreateInstanceAgileRefPromise
> {
162 return CreateInstanceInternal(localClsid
, localIid
);
165 nsCOMPtr
<nsIThread
> mtaThread(GetPersistentMTAThread());
167 return InvokeAsync(mtaThread
, __func__
, std::move(invoker
));
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();
185 SchedulerGroup::Dispatch(
186 NS_NewRunnableFunction("mscom::EnsureMTA::GetPersistentMTAThread",
187 std::move(setClearOnShutdown
)));
192 MOZ_ASSERT(sMTAData
);
194 return sMTAData
->GetThread();
198 void EnsureMTA::SyncDispatchToPersistentThread(nsIRunnable
* aRunnable
) {
199 nsCOMPtr
<nsIThread
> thread(GetPersistentMTAThread());
205 // Note that, due to APC dispatch, we might reenter this function while we
206 // wait on this event. We therefore need a unique event object for each
207 // entry into this function. If perf becomes an issue then we will want to
208 // maintain an array of events where the Nth event is unique to the Nth
210 nsAutoHandle
event(::CreateEventW(nullptr, FALSE
, FALSE
, nullptr));
215 HANDLE eventHandle
= event
.get();
216 auto eventSetter
= [&aRunnable
, eventHandle
]() -> void {
218 ::SetEvent(eventHandle
);
221 nsresult rv
= thread
->Dispatch(
222 NS_NewRunnableFunction("mscom::EnsureMTA::SyncDispatchToPersistentThread",
223 std::move(eventSetter
)),
225 MOZ_ASSERT(NS_SUCCEEDED(rv
));
230 AUTO_PROFILER_THREAD_SLEEP
;
232 while ((waitResult
= ::WaitForSingleObjectEx(event
, INFINITE
, FALSE
)) ==
233 WAIT_IO_COMPLETION
) {
235 MOZ_ASSERT(waitResult
== WAIT_OBJECT_0
);
239 * While this function currently appears to be redundant, it may become more
240 * sophisticated in the future. For example, we could optionally dispatch to an
241 * MTA context if we wanted to utilize the MTA thread pool.
244 void EnsureMTA::SyncDispatch(nsCOMPtr
<nsIRunnable
>&& aRunnable
, Option aOpt
) {
245 SyncDispatchToPersistentThread(aRunnable
);
249 } // namespace mozilla