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 (23c4fa49cc5c)

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
/* -*- 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/Unused.h"
#include "mozilla/mscom/Utils.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,
  };

  template <typename FuncT>
  explicit EnsureMTA(const 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", 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);
  }

 private:
  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(const 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", 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