EnsureMTA.cpp (5951B)
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/. */ 6 7 #include "mozilla/mscom/EnsureMTA.h" 8 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" 19 20 #include "private/pprthred.h" 21 22 namespace { 23 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; 32 } 33 }; 34 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)); 43 } 44 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(); 52 } 53 } 54 55 nsCOMPtr<nsIThread> GetThread() const { return mThread; } 56 57 private: 58 nsCOMPtr<nsIThread> mThread; 59 }; 60 61 } // anonymous namespace 62 63 namespace mozilla { 64 namespace mscom { 65 66 EnsureMTA::EnsureMTA() { 67 MOZ_ASSERT(NS_IsMainThread()); 68 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. 76 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 (void)GetPersistentMTAThread(); 87 } 88 89 return; 90 } 91 92 // In the fallback case, we simply initialize our persistent MTA thread. 93 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; 99 } 100 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); 108 } 109 110 /* static */ 111 nsCOMPtr<nsIThread> EnsureMTA::GetPersistentMTAThread() { 112 static StaticLocalAutoPtr<BackgroundMTAData> sMTAData( 113 []() -> BackgroundMTAData* { 114 BackgroundMTAData* bgData = new BackgroundMTAData(); 115 116 auto setClearOnShutdown = [ptr = &sMTAData]() -> void { 117 ClearOnShutdown(ptr, ShutdownPhase::XPCOMShutdownThreads); 118 }; 119 120 if (NS_IsMainThread()) { 121 setClearOnShutdown(); 122 return bgData; 123 } 124 125 SchedulerGroup::Dispatch( 126 NS_NewRunnableFunction("mscom::EnsureMTA::GetPersistentMTAThread", 127 std::move(setClearOnShutdown))); 128 129 return bgData; 130 }()); 131 132 MOZ_ASSERT(sMTAData); 133 134 return sMTAData->GetThread(); 135 } 136 137 /* static */ 138 void EnsureMTA::SyncDispatchToPersistentThread(nsIRunnable* aRunnable) { 139 nsCOMPtr<nsIThread> thread(GetPersistentMTAThread()); 140 MOZ_ASSERT(thread); 141 if (!thread) { 142 return; 143 } 144 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; 153 } 154 155 HANDLE eventHandle = event.get(); 156 auto eventSetter = [&aRunnable, eventHandle]() -> void { 157 aRunnable->Run(); 158 ::SetEvent(eventHandle); 159 }; 160 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; 168 } 169 170 AUTO_PROFILER_THREAD_SLEEP; 171 DWORD waitResult; 172 while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, FALSE)) == 173 WAIT_IO_COMPLETION) { 174 } 175 MOZ_ASSERT(waitResult == WAIT_OBJECT_0); 176 } 177 178 /** 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. 182 */ 183 /* static */ 184 void EnsureMTA::SyncDispatch(nsCOMPtr<nsIRunnable>&& aRunnable, Option aOpt) { 185 SyncDispatchToPersistentThread(aRunnable); 186 } 187 188 } // namespace mscom 189 } // namespace mozilla