Bug 1371376: Make mscom smart pointers destroy their objects asynchronously; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Thu, 08 Jun 2017 15:55:55 -0600
changeset 411263 3fea90e94f1df641f540a3b62e42e171f9b9ee39
parent 411262 9363eda05c395837a8c98a366e591ea33a5af240
child 411264 908eeb65a000fbe12ddb8f48cec00872451ebccb
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1371376
milestone55.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 1371376: Make mscom smart pointers destroy their objects asynchronously; r=jimm MozReview-Commit-ID: FqDVAW2Pyq2
ipc/mscom/EnsureMTA.h
ipc/mscom/Ptr.h
--- a/ipc/mscom/EnsureMTA.h
+++ b/ipc/mscom/EnsureMTA.h
@@ -11,78 +11,128 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Unused.h"
 #include "mozilla/mscom/COMApartmentRegion.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;
+
+}
 
 // This class is OK to use as a temporary on the stack.
-class MOZ_STACK_CLASS EnsureMTA
+class MOZ_STACK_CLASS EnsureMTA final
 {
 public:
   /**
    * This constructor just ensures that the MTA thread is up and running.
    */
   EnsureMTA()
   {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIThread> thread = GetMTAThread();
     MOZ_ASSERT(thread);
     Unused << thread;
   }
 
   template <typename FuncT>
   explicit EnsureMTA(const FuncT& aClosure)
   {
-    MOZ_ASSERT(NS_IsMainThread());
     if (IsCurrentThreadMTA()) {
       // We're already on the MTA, we can run aClosure directly
       aClosure();
       return;
     }
 
+    MOZ_ASSERT(NS_IsMainThread());
+
     // 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;
+    }
 
-    HANDLE event = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+    static nsAutoHandle event(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
     if (!event) {
       return;
     }
 
-    auto eventSetter = [&]() -> void {
+    HANDLE eventHandle = event.get();
+
+    auto eventSetter = [&aClosure, eventHandle]() -> void {
       aClosure();
-      ::SetEvent(event);
+      ::SetEvent(eventHandle);
     };
 
     nsresult rv =
       thread->Dispatch(NS_NewRunnableFunction(eventSetter), NS_DISPATCH_NORMAL);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ::CloseHandle(event);
+    if (NS_FAILED(rv)) {
       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();
+
+  // 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(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;
 };
 
 } // namespace mscom
 } // namespace mozilla
 
 #endif // mozilla_mscom_EnsureMTA_h
 
--- a/ipc/mscom/Ptr.h
+++ b/ipc/mscom/Ptr.h
@@ -43,45 +43,61 @@ struct MainThreadRelease
     DebugOnly<nsresult> rv =
       NS_DispatchToMainThread(NewNonOwningRunnableMethod(aPtr,
                                                          &T::Release));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 };
 
 template <typename T>
+struct MTADelete
+{
+  void operator()(T* aPtr)
+  {
+    if (!aPtr) {
+      return;
+    }
+
+    EnsureMTA::AsyncOperation([aPtr]() -> void {
+      delete aPtr;
+    });
+  }
+};
+
+template <typename T>
 struct MTARelease
 {
   void operator()(T* aPtr)
   {
     if (!aPtr) {
       return;
     }
-    EnsureMTA([&]() -> void
-    {
+
+    EnsureMTA::AsyncOperation([aPtr]() -> void {
       aPtr->Release();
     });
   }
 };
 
 template <typename T>
 struct MTAReleaseInChildProcess
 {
   void operator()(T* aPtr)
   {
     if (!aPtr) {
       return;
     }
+
     if (XRE_IsParentProcess()) {
       MOZ_ASSERT(NS_IsMainThread());
       aPtr->Release();
       return;
     }
-    EnsureMTA([&]() -> void
-    {
+
+    EnsureMTA::AsyncOperation([aPtr]() -> void {
       aPtr->Release();
     });
   }
 };
 
 struct InterceptorTargetDeleter
 {
   void operator()(IUnknown* aPtr)
@@ -94,16 +110,19 @@ struct InterceptorTargetDeleter
 
 template <typename T>
 using STAUniquePtr = mozilla::UniquePtr<T, detail::MainThreadRelease<T>>;
 
 template <typename T>
 using MTAUniquePtr = mozilla::UniquePtr<T, detail::MTARelease<T>>;
 
 template <typename T>
+using MTADeletePtr = mozilla::UniquePtr<T, detail::MTADelete<T>>;
+
+template <typename T>
 using ProxyUniquePtr = mozilla::UniquePtr<T, detail::MTAReleaseInChildProcess<T>>;
 
 template <typename T>
 using InterceptorTargetPtr =
   mozilla::UniquePtr<T, detail::InterceptorTargetDeleter>;
 
 namespace detail {