Bug 1261107: Add EnsureMTA class to ipc/mscom/Utils. This class synchronously executes a function on a background thread that resides in Microsoft COM's multithreaded apartment; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Mon, 18 Jul 2016 13:49:28 -0600
changeset 306850 e87f3cbb950fe22a5e0c80fda8494b8ea68a4203
parent 306849 e108877f47de83c38c245c0683120b1cdee653c7
child 306851 23d63b3b2c751ec56b92c2f52c8cfd3a13096145
push id30862
push useraklotz@mozilla.com
push dateWed, 27 Jul 2016 18:14:27 +0000
treeherderautoland@23d63b3b2c75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1261107
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1261107: Add EnsureMTA class to ipc/mscom/Utils. This class synchronously executes a function on a background thread that resides in Microsoft COM's multithreaded apartment; r=jimm MozReview-Commit-ID: KTE1VdCYS0O
ipc/mscom/EnsureMTA.cpp
ipc/mscom/EnsureMTA.h
ipc/mscom/moz.build
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/EnsureMTA.cpp
@@ -0,0 +1,78 @@
+/* -*- 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/StaticPtr.h"
+#include "nsThreadUtils.h"
+
+#include "private/pprthred.h"
+
+namespace {
+
+class EnterMTARunnable : public mozilla::Runnable
+{
+public:
+  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();
+    nsresult rv = NS_NewNamedThread("COM MTA",
+                                    getter_AddRefs(mThread), runnable);
+    NS_WARN_IF(NS_FAILED(rv));
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+
+  ~BackgroundMTAData()
+  {
+    if (mThread) {
+      mThread->Dispatch(NS_NewRunnableFunction(&::CoUninitialize),
+                        NS_DISPATCH_NORMAL);
+      mThread->Shutdown();
+    }
+  }
+
+  nsCOMPtr<nsIThread> GetThread() const
+  {
+    return mThread;
+  }
+
+private:
+  nsCOMPtr<nsIThread> mThread;
+};
+
+} // anonymous namespace
+
+static mozilla::StaticAutoPtr<BackgroundMTAData> sMTAData;
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ nsCOMPtr<nsIThread>
+EnsureMTA::GetMTAThread()
+{
+  if (!sMTAData) {
+    sMTAData = new BackgroundMTAData();
+    ClearOnShutdown(&sMTAData, ShutdownPhase::ShutdownThreads);
+  }
+  return sMTAData->GetThread();
+}
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/EnsureMTA.h
@@ -0,0 +1,77 @@
+/* -*- 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/Function.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+#include "mozilla/mscom/utils.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace mscom {
+
+// This class is OK to use as a temporary on the stack.
+class MOZ_STACK_CLASS EnsureMTA
+{
+public:
+  template <typename FuncT>
+  EnsureMTA(const FuncT& aClosure)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (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);
+
+    HANDLE event = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+    if (!event) {
+      return;
+    }
+
+    auto eventSetter = [&]() -> void {
+      aClosure();
+      ::SetEvent(event);
+    };
+
+    nsresult rv =
+      thread->Dispatch(NS_NewRunnableFunction(eventSetter), NS_DISPATCH_NORMAL);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ::CloseHandle(event);
+      return;
+    }
+
+    DWORD waitResult;
+    while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, TRUE)) ==
+           WAIT_IO_COMPLETION) {
+    }
+    MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
+    ::CloseHandle(event);
+  }
+
+private:
+  static nsCOMPtr<nsIThread> GetMTAThread();
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_EnsureMTA_h
+
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -1,23 +1,25 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.mscom += [
     'COMApartmentRegion.h',
+    'EnsureMTA.h',
     'MainThreadRuntime.h',
     'Utils.h',
 ]
 
 SOURCES += [
     'Utils.cpp',
 ]
 
 UNIFIED_SOURCES += [
+    'EnsureMTA.cpp',
     'MainThreadRuntime.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'