Bug 1237794: Extend ClearOnShutdown() to allow specifying the shutdown phase r=froyd

This commit is contained in:
Randell Jesup 2016-01-15 13:12:07 -05:00
parent b177199bda
commit 5cb8d2ec15
3 changed files with 74 additions and 29 deletions

View File

@ -9,8 +9,37 @@
namespace mozilla {
namespace ClearOnShutdown_Internal {
bool sHasShutDown = false;
StaticAutoPtr<LinkedList<ShutdownObserver>> sShutdownObservers;
Array<StaticAutoPtr<ShutdownList>,
static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
ShutdownPhase sCurrentShutdownPhase = ShutdownPhase::NotInShutdown;
} // 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());
// Shutdown only goes one direction...
MOZ_ASSERT(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase));
// It's impossible to add an entry for a "past" phase; this is blocked in
// ClearOnShutdown, but clear them out anyways in case there are phases
// that weren't passed to KillClearOnShutdown.
for (size_t phase = static_cast<size_t>(ShutdownPhase::First);
phase <= static_cast<size_t>(aPhase);
phase++) {
if (sShutdownObservers[static_cast<size_t>(phase)]) {
while (ShutdownObserver* observer = sShutdownObservers[static_cast<size_t>(phase)]->popFirst()) {
observer->Shutdown();
delete observer;
}
sShutdownObservers[static_cast<size_t>(phase)] = nullptr;
}
}
}
} // namespace mozilla

View File

@ -9,16 +9,21 @@
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Array.h"
#include "MainThreadUtils.h"
/*
* This header exports one public method in the mozilla namespace:
*
* template<class SmartPtr>
* void ClearOnShutdown(SmartPtr *aPtr)
* void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
*
* This function takes a pointer to a smart pointer and nulls the smart pointer
* on shutdown.
* 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
* cause a MOZ_ASSERT. In case a phase is not explicitly cleared we will
* clear it on the next phase that occurs.
*
* This is useful if you have a global smart pointer object which you don't
* want to "leak" on shutdown.
@ -36,6 +41,20 @@
*/
namespace mozilla {
// Must be contiguous starting at 0
enum class ShutdownPhase {
NotInShutdown = 0,
WillShutdown,
Shutdown,
ShutdownThreads,
ShutdownLoaders,
ShutdownFinal,
ShutdownPhase_Length, // never pass this value
First = WillShutdown, // for iteration
Last = ShutdownFinal
};
namespace ClearOnShutdown_Internal {
class ShutdownObserver : public LinkedListElement<ShutdownObserver>
@ -67,45 +86,38 @@ private:
SmartPtr* mPtr;
};
extern bool sHasShutDown;
extern StaticAutoPtr<LinkedList<ShutdownObserver>> sShutdownObservers;
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)
ClearOnShutdown(SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal)
{
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sHasShutDown);
MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
if (!sShutdownObservers) {
sShutdownObservers = new LinkedList<ShutdownObserver>();
// 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;
}
sShutdownObservers->insertBack(new PointerClearer<SmartPtr>(aPtr));
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));
}
// Called when XPCOM is shutting down, after all shutdown notifications have
// been sent and after all threads' event loops have been purged.
inline void
KillClearOnShutdown()
{
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());
if (sShutdownObservers) {
while (ShutdownObserver* observer = sShutdownObservers->popFirst()) {
observer->Shutdown();
delete observer;
}
}
sShutdownObservers = nullptr;
sHasShutDown = true;
}
void KillClearOnShutdown(ShutdownPhase aPhase);
} // namespace mozilla

View File

@ -832,6 +832,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr)
(nsObserverService**)getter_AddRefs(observerService));
if (observerService) {
mozilla::KillClearOnShutdown(ShutdownPhase::WillShutdown);
observerService->NotifyObservers(nullptr,
NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
nullptr);
@ -839,6 +840,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr)
nsCOMPtr<nsIServiceManager> mgr;
rv = NS_GetServiceManager(getter_AddRefs(mgr));
if (NS_SUCCEEDED(rv)) {
mozilla::KillClearOnShutdown(ShutdownPhase::Shutdown);
observerService->NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
nullptr);
}
@ -851,6 +853,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr)
mozilla::scache::StartupCache::DeleteSingleton();
if (observerService)
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownThreads);
observerService->NotifyObservers(nullptr,
NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
nullptr);
@ -881,6 +884,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr)
// We save the "xpcom-shutdown-loaders" observers to notify after
// the observerservice is gone.
if (observerService) {
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownLoaders);
observerService->EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
getter_AddRefs(moduleLoaders));
@ -891,7 +895,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr)
// Free ClearOnShutdown()'ed smart pointers. This needs to happen *after*
// we've finished notifying observers of XPCOM shutdown, because shutdown
// observers themselves might call ClearOnShutdown().
mozilla::KillClearOnShutdown();
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
// XPCOM is officially in shutdown mode NOW
// Set this only after the observers have been notified as this