Bug 1573623: Add RunOnShutdown, a variant of ClearOnShutdown which supports passing callables; r=froydnj
authorAaron Klotz <aklotz@mozilla.com>
Thu, 15 Aug 2019 19:08:39 +0000
changeset 488327 f510b32aec3a54efcf58e16f5c5813e2809dd901
parent 488326 fa80b89e0ce4969bed9ffe465c726b99878d94ae
child 488328 e39c5a2cde3a3693ad40e40362c365a8a47d6a59
push id36440
push userncsoregi@mozilla.com
push dateFri, 16 Aug 2019 03:57:48 +0000
treeherdermozilla-central@a58b7dc85887 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1573623
milestone70.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 1573623: Add RunOnShutdown, a variant of ClearOnShutdown which supports passing callables; r=froydnj This patch adds `RunOnShutdown`, which allows the caller to supply any callable to be invoked during the specified shutdown phase. This allows us to do more than just clear smart pointers without needing to write a bunch of observer service boilerplate. We use `std::function` to hold the callable. Differential Revision: https://phabricator.services.mozilla.com/D41816
xpcom/base/ClearOnShutdown.cpp
xpcom/base/ClearOnShutdown.h
--- a/xpcom/base/ClearOnShutdown.cpp
+++ b/xpcom/base/ClearOnShutdown.cpp
@@ -9,16 +9,32 @@
 namespace mozilla {
 namespace ClearOnShutdown_Internal {
 
 Array<StaticAutoPtr<ShutdownList>,
       static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)>
     sShutdownObservers;
 ShutdownPhase sCurrentShutdownPhase = ShutdownPhase::NotInShutdown;
 
+void InsertIntoShutdownList(ShutdownObserver* aObserver, ShutdownPhase aPhase) {
+  // Adding a ClearOnShutdown for a "past" phase is an error.
+  if (!(static_cast<size_t>(sCurrentShutdownPhase) <
+        static_cast<size_t>(aPhase))) {
+    MOZ_ASSERT(false, "ClearOnShutdown for phase that already was cleared");
+    aObserver->Shutdown();
+    delete aObserver;
+    return;
+  }
+
+  if (!(sShutdownObservers[static_cast<size_t>(aPhase)])) {
+    sShutdownObservers[static_cast<size_t>(aPhase)] = new ShutdownList();
+  }
+  sShutdownObservers[static_cast<size_t>(aPhase)]->insertBack(aObserver);
+}
+
 }  // namespace ClearOnShutdown_Internal
 
 // Called when XPCOM is shutting down, after all shutdown notifications have
 // been sent and after all threads' event loops have been purged.
 void KillClearOnShutdown(ShutdownPhase aPhase) {
   using namespace ClearOnShutdown_Internal;
 
   MOZ_ASSERT(NS_IsMainThread());
--- a/xpcom/base/ClearOnShutdown.h
+++ b/xpcom/base/ClearOnShutdown.h
@@ -7,18 +7,20 @@
 #ifndef mozilla_ClearOnShutdown_h
 #define mozilla_ClearOnShutdown_h
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Array.h"
 #include "MainThreadUtils.h"
 
+#include <functional>
+
 /*
- * This header exports one public method in the mozilla namespace:
+ * This header exports two public methods in the mozilla namespace:
  *
  *   template<class SmartPtr>
  *   void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
  *
  * This function takes a pointer to a smart pointer and nulls the smart pointer
  * on shutdown (and a particular phase of shutdown as needed).  If a phase
  * is specified, the ptr will be cleared at the start of that phase.  Also,
  * if a phase has already occurred when ClearOnShutdown() is called it will
@@ -30,19 +32,28 @@
  *
  * Although ClearOnShutdown will work with any smart pointer (i.e., nsCOMPtr,
  * nsRefPtr, nsAutoPtr, StaticRefPtr, and StaticAutoPtr), you probably want to
  * use it only with StaticRefPtr and StaticAutoPtr.  There is no way to undo a
  * call to ClearOnShutdown, so you can call it only on smart pointers which you
  * know will live until the program shuts down.  In practice, these are likely
  * global variables, which should be Static{Ref,Auto}Ptr.
  *
- * ClearOnShutdown is currently main-thread only because we don't want to
- * accidentally free an object from a different thread than the one it was
- * created on.
+ *   template <typename CallableT>
+ *   void RunOnShutdown(CallableT&& aCallable,
+ *                      aPhase = ShutdownPhase::ShutdownFinal)
+ *
+ * This function takes a callable and executes it upon shutdown at the start of
+ * the specified phase. If the phase has already occurred when RunOnShutdown()
+ * is called, it will cause a MOZ_ASSERT. In case a phase is not explicitly
+ * cleared, we will clear it on the next phase that occurs.
+ *
+ * ClearOnShutdown and RunOnShutdown are both currently main-thread only because
+ * we don't want to accidentally free an object from a different thread than the
+ * one it was created on.
  */
 
 namespace mozilla {
 
 // Must be contiguous starting at 0
 enum class ShutdownPhase {
   NotInShutdown = 0,
   WillShutdown,
@@ -74,45 +85,66 @@ class PointerClearer : public ShutdownOb
       *mPtr = nullptr;
     }
   }
 
  private:
   SmartPtr* mPtr;
 };
 
+class FunctionInvoker : public ShutdownObserver {
+ public:
+  template <typename CallableT>
+  explicit FunctionInvoker(CallableT&& aCallable)
+      : mCallable(std::forward<CallableT>(aCallable)) {}
+
+  virtual void Shutdown() override {
+    if (!mCallable) {
+      return;
+    }
+
+    mCallable();
+  }
+
+ private:
+  std::function<void()> mCallable;
+};
+
+void InsertIntoShutdownList(ShutdownObserver* aShutdownObserver,
+                            ShutdownPhase aPhase);
+
 typedef LinkedList<ShutdownObserver> ShutdownList;
 extern Array<StaticAutoPtr<ShutdownList>,
              static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)>
     sShutdownObservers;
 extern ShutdownPhase sCurrentShutdownPhase;
 
 }  // namespace ClearOnShutdown_Internal
 
 template <class SmartPtr>
 inline void ClearOnShutdown(
     SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal) {
   using namespace ClearOnShutdown_Internal;
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
 
-  // Adding a ClearOnShutdown for a "past" phase is an error.
-  if (!(static_cast<size_t>(sCurrentShutdownPhase) <
-        static_cast<size_t>(aPhase))) {
-    MOZ_ASSERT(false, "ClearOnShutdown for phase that already was cleared");
-    *aPtr = nullptr;
-    return;
-  }
+  InsertIntoShutdownList(new PointerClearer<SmartPtr>(aPtr), aPhase);
+}
 
-  if (!(sShutdownObservers[static_cast<size_t>(aPhase)])) {
-    sShutdownObservers[static_cast<size_t>(aPhase)] = new ShutdownList();
-  }
-  sShutdownObservers[static_cast<size_t>(aPhase)]->insertBack(
-      new PointerClearer<SmartPtr>(aPtr));
+template <typename CallableT>
+inline void RunOnShutdown(CallableT&& aCallable,
+                          ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal) {
+  using namespace ClearOnShutdown_Internal;
+
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
+
+  InsertIntoShutdownList(
+      new FunctionInvoker(std::forward<CallableT>(aCallable)), aPhase);
 }
 
 // Called when XPCOM is shutting down, after all shutdown notifications have
 // been sent and after all threads' event loops have been purged.
 void KillClearOnShutdown(ShutdownPhase aPhase);
 
 }  // namespace mozilla