tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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