DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (0d24f14c0847)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/mscom/EnsureMTA.h"

#include "mozilla/ClearOnShutdown.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/StaticLocalPtr.h"
#include "mozilla/SystemGroup.h"
#include "nsThreadUtils.h"

#include "private/pprthred.h"

namespace {

class EnterMTARunnable : public mozilla::Runnable {
 public:
  EnterMTARunnable() : mozilla::Runnable("EnterMTARunnable") {}
  NS_IMETHOD Run() override {
    mozilla::DebugOnly<HRESULT> hr =
        ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    MOZ_ASSERT(SUCCEEDED(hr));
    return NS_OK;
  }
};

class BackgroundMTAData {
 public:
  BackgroundMTAData() {
    nsCOMPtr<nsIRunnable> runnable = new EnterMTARunnable();
    mozilla::DebugOnly<nsresult> rv =
        NS_NewNamedThread("COM MTA", getter_AddRefs(mThread), runnable);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_NewNamedThread failed");
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }

  ~BackgroundMTAData() {
    if (mThread) {
      mThread->Dispatch(
          NS_NewRunnableFunction("BackgroundMTAData::~BackgroundMTAData",
                                 &::CoUninitialize),
          NS_DISPATCH_NORMAL);
      mThread->Shutdown();
    }
  }

  nsCOMPtr<nsIThread> GetThread() const { return mThread; }

 private:
  nsCOMPtr<nsIThread> mThread;
};

}  // anonymous namespace

namespace mozilla {
namespace mscom {

/* static */
RefPtr<EnsureMTA::CreateInstanceAgileRefPromise>
EnsureMTA::CreateInstanceInternal(REFCLSID aClsid, REFIID aIid) {
  MOZ_ASSERT(IsCurrentThreadExplicitMTA());

  RefPtr<IUnknown> iface;
  HRESULT hr = ::CoCreateInstance(aClsid, nullptr, CLSCTX_INPROC_SERVER, aIid,
                                  getter_AddRefs(iface));
  if (FAILED(hr)) {
    return CreateInstanceAgileRefPromise::CreateAndReject(hr, __func__);
  }

  // We need to use the two argument constructor for AgileReference because our
  // RefPtr is not parameterized on the specific interface being requested.
  AgileReference agileRef(aIid, iface);
  if (!agileRef) {
    return CreateInstanceAgileRefPromise::CreateAndReject(agileRef.GetHResult(),
                                                          __func__);
  }

  return CreateInstanceAgileRefPromise::CreateAndResolve(std::move(agileRef),
                                                         __func__);
}

/* static */
RefPtr<EnsureMTA::CreateInstanceAgileRefPromise> EnsureMTA::CreateInstance(
    REFCLSID aClsid, REFIID aIid) {
  MOZ_ASSERT(IsCOMInitializedOnCurrentThread());

  const bool isClassOk = IsClassThreadAwareInprocServer(aClsid);
  MOZ_ASSERT(isClassOk,
             "mozilla::mscom::EnsureMTA::CreateInstance is not "
             "safe/performant/necessary to use with this CLSID. This CLSID "
             "either does not support creation from within a multithreaded "
             "apartment, or it is not an in-process server.");
  if (!isClassOk) {
    return CreateInstanceAgileRefPromise::CreateAndReject(CO_E_NOT_SUPPORTED,
                                                          __func__);
  }

  if (IsCurrentThreadExplicitMTA()) {
    // It's safe to immediately call CreateInstanceInternal
    return CreateInstanceInternal(aClsid, aIid);
  }

  // aClsid and aIid are references. Make local copies that we can put into the
  // lambda in case the sources of aClsid or aIid are not static data
  CLSID localClsid = aClsid;
  IID localIid = aIid;

  auto invoker = [localClsid,
                  localIid]() -> RefPtr<CreateInstanceAgileRefPromise> {
    return CreateInstanceInternal(localClsid, localIid);
  };

  nsCOMPtr<nsIThread> mtaThread(GetMTAThread());

  return InvokeAsync(mtaThread->SerialEventTarget(), __func__,
                     std::move(invoker));
}

/* static */
nsCOMPtr<nsIThread> EnsureMTA::GetMTAThread() {
  static StaticLocalAutoPtr<BackgroundMTAData> sMTAData(
      []() -> BackgroundMTAData* {
        BackgroundMTAData* bgData = new BackgroundMTAData();

        auto setClearOnShutdown = [ptr = &sMTAData]() -> void {
          ClearOnShutdown(ptr, ShutdownPhase::ShutdownThreads);
        };

        if (NS_IsMainThread()) {
          setClearOnShutdown();
          return bgData;
        }

        SystemGroup::Dispatch(
            TaskCategory::Other,
            NS_NewRunnableFunction("mscom::EnsureMTA::GetMTAThread",
                                   std::move(setClearOnShutdown)));

        return bgData;
      }());

  MOZ_ASSERT(sMTAData);

  return sMTAData->GetThread();
}

}  // namespace mscom
}  // namespace mozilla