1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_ClearOnShutdown_h
8 #define mozilla_ClearOnShutdown_h
9 
10 #include "mozilla/LinkedList.h"
11 #include "mozilla/StaticPtr.h"
12 #include "mozilla/Array.h"
13 #include "ShutdownPhase.h"
14 #include "MainThreadUtils.h"
15 
16 #include <functional>
17 
18 /*
19  * This header exports two public methods in the mozilla namespace:
20  *
21  *   template<class SmartPtr>
22  *   void ClearOnShutdown(SmartPtr *aPtr,
23  * aPhase=ShutdownPhase::XPCOMShutdownFinal)
24  *
25  * This function takes a pointer to a smart pointer and nulls the smart pointer
26  * on shutdown (and a particular phase of shutdown as needed).  If a phase
27  * is specified, the ptr will be cleared at the start of that phase.  Also,
28  * if a phase has already occurred when ClearOnShutdown() is called it will
29  * cause a MOZ_ASSERT.  In case a phase is not explicitly cleared we will
30  * clear it on the next phase that occurs.
31  *
32  * This is useful if you have a global smart pointer object which you don't
33  * want to "leak" on shutdown.
34  *
35  * Although ClearOnShutdown will work with any smart pointer (i.e., nsCOMPtr,
36  * RefPtr, StaticRefPtr, and StaticAutoPtr), you probably want to
37  * use it only with StaticRefPtr and StaticAutoPtr.  There is no way to undo a
38  * call to ClearOnShutdown, so you can call it only on smart pointers which you
39  * know will live until the program shuts down.  In practice, these are likely
40  * global variables, which should be Static{Ref,Auto}Ptr.
41  *
42  *   template <typename CallableT>
43  *   void RunOnShutdown(CallableT&& aCallable,
44  *                      aPhase = ShutdownPhase::XPCOMShutdownFinal)
45  *
46  * This function takes a callable and executes it upon shutdown at the start of
47  * the specified phase. If the phase has already occurred when RunOnShutdown()
48  * is called, it will cause a MOZ_ASSERT. In case a phase is not explicitly
49  * cleared, we will clear it on the next phase that occurs.
50  *
51  * ClearOnShutdown and RunOnShutdown are both currently main-thread only because
52  * we don't want to accidentally free an object from a different thread than the
53  * one it was created on.
54  */
55 
56 namespace mozilla {
57 
58 namespace ClearOnShutdown_Internal {
59 
60 class ShutdownObserver : public LinkedListElement<ShutdownObserver> {
61  public:
62   virtual void Shutdown() = 0;
63   virtual ~ShutdownObserver() = default;
64 };
65 
66 template <class SmartPtr>
67 class PointerClearer : public ShutdownObserver {
68  public:
PointerClearer(SmartPtr * aPtr)69   explicit PointerClearer(SmartPtr* aPtr) : mPtr(aPtr) {}
70 
Shutdown()71   virtual void Shutdown() override {
72     if (mPtr) {
73       *mPtr = nullptr;
74     }
75   }
76 
77  private:
78   SmartPtr* mPtr;
79 };
80 
81 class FunctionInvoker : public ShutdownObserver {
82  public:
83   template <typename CallableT>
FunctionInvoker(CallableT && aCallable)84   explicit FunctionInvoker(CallableT&& aCallable)
85       : mCallable(std::forward<CallableT>(aCallable)) {}
86 
Shutdown()87   virtual void Shutdown() override {
88     if (!mCallable) {
89       return;
90     }
91 
92     mCallable();
93   }
94 
95  private:
96   std::function<void()> mCallable;
97 };
98 
99 void InsertIntoShutdownList(ShutdownObserver* aShutdownObserver,
100                             ShutdownPhase aPhase);
101 
102 typedef LinkedList<ShutdownObserver> ShutdownList;
103 extern Array<StaticAutoPtr<ShutdownList>,
104              static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)>
105     sShutdownObservers;
106 extern ShutdownPhase sCurrentClearOnShutdownPhase;
107 
108 }  // namespace ClearOnShutdown_Internal
109 
110 template <class SmartPtr>
111 inline void ClearOnShutdown(
112     SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::XPCOMShutdownFinal) {
113   using namespace ClearOnShutdown_Internal;
114 
115   MOZ_ASSERT(NS_IsMainThread());
116   MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
117 
118   InsertIntoShutdownList(new PointerClearer<SmartPtr>(aPtr), aPhase);
119 }
120 
121 template <typename CallableT>
122 inline void RunOnShutdown(
123     CallableT&& aCallable,
124     ShutdownPhase aPhase = ShutdownPhase::XPCOMShutdownFinal) {
125   using namespace ClearOnShutdown_Internal;
126 
127   MOZ_ASSERT(NS_IsMainThread());
128   MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
129 
130   InsertIntoShutdownList(
131       new FunctionInvoker(std::forward<CallableT>(aCallable)), aPhase);
132 }
133 
PastShutdownPhase(ShutdownPhase aPhase)134 inline bool PastShutdownPhase(ShutdownPhase aPhase) {
135   MOZ_ASSERT(NS_IsMainThread());
136 
137   return size_t(ClearOnShutdown_Internal::sCurrentClearOnShutdownPhase) >=
138          size_t(aPhase);
139 }
140 
141 // Called when XPCOM is shutting down, after all shutdown notifications have
142 // been sent and after all threads' event loops have been purged.
143 void KillClearOnShutdown(ShutdownPhase aPhase);
144 
145 }  // namespace mozilla
146 
147 #endif
148