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.

Implementation

Mercurial (3d02a4c69a81)

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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
/* -*- 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/. */

#ifndef mozilla_mscom_EnsureMTA_h
#define mozilla_mscom_EnsureMTA_h

#include "MainThreadUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Unused.h"
#include "mozilla/mscom/AgileReference.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "nsWindowsHelpers.h"

#include <windows.h>

namespace mozilla {
namespace mscom {
namespace detail {

// Forward declarations
template <typename T>
struct MTADelete;

template <typename T>
struct MTARelease;

template <typename T>
struct MTAReleaseInChildProcess;

struct PreservedStreamDeleter;

}  // namespace detail

// This class is OK to use as a temporary on the stack.
class MOZ_STACK_CLASS EnsureMTA final {
 public:
  /**
   * This constructor just ensures that the MTA thread is up and running.
   */
  EnsureMTA() {
    nsCOMPtr<nsIThread> thread = GetMTAThread();
    MOZ_ASSERT(thread);
    Unused << thread;
  }

  enum class Option {
    Default,
    // Forcibly dispatch to the thread returned by GetMTAThread(), even if the
    // current thread is already inside a MTA.
    ForceDispatch,
  };

  /**
   * Synchronously run |aClosure| on a thread living in the COM multithreaded
   * apartment. If the current thread lives inside the COM MTA, then it runs
   * |aClosure| immediately unless |aOpt| == Option::ForceDispatch.
   */
  template <typename FuncT>
  explicit EnsureMTA(FuncT&& aClosure, Option aOpt = Option::Default) {
    if (aOpt != Option::ForceDispatch && IsCurrentThreadMTA()) {
      // We're already on the MTA, we can run aClosure directly
      aClosure();
      return;
    }

    // In this case we need to run aClosure on a background thread in the MTA
    nsCOMPtr<nsIThread> thread = GetMTAThread();
    MOZ_ASSERT(thread);
    if (!thread) {
      return;
    }

    // Note that we might reenter the EnsureMTA constructor while we wait on
    // this event due to APC dispatch, therefore we need a unique event object
    // for each entry. If perf becomes an issue then we will want to maintain
    // an array of events where the Nth event is unique to the Nth reentry.
    nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
    if (!event) {
      return;
    }

    HANDLE eventHandle = event.get();

    auto eventSetter = [&aClosure, eventHandle]() -> void {
      aClosure();
      ::SetEvent(eventHandle);
    };

    nsresult rv = thread->Dispatch(
        NS_NewRunnableFunction("EnsureMTA", std::move(eventSetter)),
        NS_DISPATCH_NORMAL);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    if (NS_FAILED(rv)) {
      return;
    }

    DWORD waitResult;
    while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, TRUE)) ==
           WAIT_IO_COMPLETION) {
    }
    MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
  }

  using CreateInstanceAgileRefPromise =
      MozPromise<AgileReference, HRESULT, false>;

  /**
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   *
   * Asynchronously instantiate a new COM object from a MTA thread, unless the
   * current thread is already living inside the multithreaded apartment, in
   * which case the object is immediately instantiated.
   *
   * This function only supports the most common configurations for creating
   * a new object, so it only supports in-process servers. Furthermore, this
   * function does not support aggregation (ie. the |pUnkOuter| parameter to
   * CoCreateInstance).
   *
   * Given that attempting to instantiate an Apartment-threaded COM object
   * inside the MTA results in a *loss* of performance, we assert when that
   * situation arises.
   *
   * The resulting promise, once resolved, provides an AgileReference that may
   * be passed between any COM-initialized thread in the current process.
   *
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   *
   * WARNING:
   * Some COM objects do not support creation in the multithreaded apartment,
   * in which case this function is not available as an option. In this case,
   * the promise will always be rejected. In debug builds we will assert.
   *
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   *
   * WARNING:
   * Any in-process COM objects whose interfaces accept HWNDs are probably
   * *not* safe to instantiate in the multithreaded apartment! Even if this
   * function succeeds when creating such an object, you *MUST NOT* do so, as
   * these failures might not become apparent until your code is running out in
   * the wild on the release channel!
   *
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   *
   * WARNING:
   * When you obtain an interface from the AgileReference, it may or may not be
   * a proxy to the real object. This depends entirely on the implementation of
   * the underlying class and the multithreading capabilities that the class
   * declares to the COM runtime. If the interface is proxied, it might be
   * expensive to invoke methods on that interface! *Always* test the
   * performance of your method calls when calling interfaces that are resolved
   * via this function!
   *
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   *
   * (Despite this myriad of warnings, it is still *much* safer to use this
   * function to asynchronously create COM objects than it is to roll your own!)
   *
   *       *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
   */
  static RefPtr<CreateInstanceAgileRefPromise> CreateInstance(REFCLSID aClsid,
                                                              REFIID aIid);

 private:
  static RefPtr<CreateInstanceAgileRefPromise> CreateInstanceInternal(
      REFCLSID aClsid, REFIID aIid);

  static nsCOMPtr<nsIThread> GetMTAThread();

  // The following function is private in order to force any consumers to be
  // declared as friends of EnsureMTA. The intention is to prevent
  // AsyncOperation from becoming some kind of free-for-all mechanism for
  // asynchronously executing work on a background thread.
  template <typename FuncT>
  static void AsyncOperation(FuncT&& aClosure) {
    if (IsCurrentThreadMTA()) {
      aClosure();
      return;
    }

    nsCOMPtr<nsIThread> thread(GetMTAThread());
    MOZ_ASSERT(thread);
    if (!thread) {
      return;
    }

    DebugOnly<nsresult> rv = thread->Dispatch(
        NS_NewRunnableFunction("mscom::EnsureMTA::AsyncOperation",
                               std::move(aClosure)),
        NS_DISPATCH_NORMAL);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }

  template <typename T>
  friend struct mozilla::mscom::detail::MTADelete;

  template <typename T>
  friend struct mozilla::mscom::detail::MTARelease;

  template <typename T>
  friend struct mozilla::mscom::detail::MTAReleaseInChildProcess;

  friend struct mozilla::mscom::detail::PreservedStreamDeleter;
};

}  // namespace mscom
}  // namespace mozilla

#endif  // mozilla_mscom_EnsureMTA_h