Bug 789945 - part 2: add asyncshutdown blocker, r?bsmedberg
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -65,16 +65,33 @@
#define ENSURE_MAIN_PROCESS(message, pref) \
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
return NS_ERROR_NOT_AVAILABLE; \
}
#endif
class PrefCallback;
+namespace {
+already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
+ nsCOMPtr<nsIAsyncShutdownService> svc = mozilla::services::GetAsyncShutdown();
+ MOZ_RELEASE_ASSERT(svc);
+
+ nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
+ nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
+ if (!shutdownPhase) {
+ // In the content process, we don't need to (and can't) save anything.
+ return nullptr;
+ }
+ MOZ_RELEASE_ASSERT(shutdownPhase);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ return shutdownPhase.forget();
+}
+}
+
namespace mozilla {
// Definitions
#define INITIAL_PREF_FILES 10
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
// Prototypes
static nsresult openPrefFile(nsIFile* aFile);
@@ -202,17 +219,24 @@ class PreferenceRunnableSaveDone final :
{
public:
PreferenceRunnableSaveDone() {}
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
if (Preferences::sPreferences) {
Preferences::sPreferences->mCurrentOMTSaver = nullptr;
Preferences::sPreferences->mCurrentOMTSaveTimer = nullptr;
- if (gDirty) {
+ if (Preferences::sPreferences->mClearShutdownBlockerWhenOMTSaveIsDone) {
+ nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
+ shutdownPhase->RemoveBlocker(
+ Preferences::sPreferences->mShutdownBlocker);
+ }
+ // NB: else because we do not want to restart the save if we're shutting
+ // down.
+ else if (gDirty) {
Preferences::sPreferences->SavePrefsToDefaultFileOMT();
}
}
return NS_OK;
}
};
class PreferenceRunnableSaver final : public nsRunnable
@@ -581,18 +605,19 @@ Preferences::Shutdown()
//-----------------------------------------------------------------------------
/*
* Constructor/Destructor
*/
Preferences::Preferences()
- : mAreWaitingForOMTSave(false),
- mOMTSaveMonitor("Preferences::OMTSaveMonitor")
+ : mAreWaitingForOMTSave(false)
+ , mOMTSaveMonitor("Preferences::OMTSaveMonitor")
+ , mClearShutdownBlockerWhenOMTSaveIsDone(false)
{
}
Preferences::~Preferences()
{
NS_ASSERTION(sPreferences == this, "Isn't this the singleton instance?");
delete gObserverTable;
@@ -669,24 +694,22 @@ Preferences::Init()
static_cast<nsISupports *>(static_cast<void *>(this)),
"pref-config-startup");
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
- rv = observerService->AddObserver(this, "profile-before-change", true);
-
observerService->AddObserver(this, "load-extension-defaults", true);
observerService->AddObserver(this, "suspend_process_notification", true);
gDirtyCallback = &PrefsDirtyCallback;
- return(rv);
+ return NS_OK;
}
// static
nsresult
Preferences::ResetAndReadUserPrefs()
{
sPreferences->ResetUserPrefs();
return sPreferences->ReadUserPrefs(nullptr);
@@ -696,19 +719,17 @@ NS_IMETHODIMP
Preferences::Observe(nsISupports *aSubject, const char *aTopic,
const char16_t *someData)
{
if (XRE_IsContentProcess())
return NS_ERROR_NOT_AVAILABLE;
nsresult rv = NS_OK;
- if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
- rv = SavePrefFile(nullptr);
- } else if (!strcmp(aTopic, "load-extension-defaults")) {
+ if (!strcmp(aTopic, "load-extension-defaults")) {
pref_LoadPrefsInDirList(NS_EXT_PREFS_DEFAULTS_DIR_LIST);
} else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
// Reload the default prefs from file.
pref_InitInitialObjects();
} else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
// Our process is being suspended. The OS may wake our process later,
// or it may kill the process. In case our process is going to be killed
// from the suspended state, we save preferences before suspending.
@@ -1013,16 +1034,26 @@ Preferences::ReadAndOwnUserPrefFile(nsIF
rv = NS_ERROR_FILE_NOT_FOUND;
}
// If we had a current file before, save now if we're dirty
if (gDirty && hadCurrentFile) {
SavePrefsToDefaultFileOMT();
}
+ if (!hadCurrentFile) {
+ mShutdownBlocker = new PrefShutdownBlocker(
+ NS_LITERAL_STRING("Pref service shutdown blocker"));
+ nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
+ shutdownPhase->AddBlocker(mShutdownBlocker,
+ NS_LITERAL_STRING(__FILE__),
+ __LINE__,
+ NS_LITERAL_STRING("Pref service shutdown blocker"));
+ }
+
return rv;
}
nsresult
Preferences::SavePrefFileInternal(nsIFile *aFile)
{
if (nullptr == aFile) {
// the gDirty flag tells us if we should write to mCurrentFile
@@ -2079,11 +2110,43 @@ int32_t
Preferences::GetDefaultType(const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
int32_t result;
return NS_SUCCEEDED(sDefaultRootBranch->GetPrefType(aPref, &result)) ?
result : nsIPrefBranch::PREF_INVALID;
}
+// Shutdown blocker code
+
+NS_IMPL_ISUPPORTS(PrefShutdownBlocker, nsIAsyncShutdownBlocker)
+
+NS_IMETHODIMP
+PrefShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* client)
+{
+ nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
+ // Ensure we're still alive. If not, we should just clear out.
+ if (Preferences::sPreferences) {
+ if (gDirty) {
+ // This takes care of any async saves we might still be waiting for, too.
+ Preferences::sPreferences->SavePrefFileInternal(nullptr);
+ }
+ // Now we know we're not dirty. We can't be waiting for an async save
+ // that hasn't moved onto the STS thread yet, because we're not dirty,
+ // the only other edge case we care about is if we're in the middle of
+ // an async save, in which case we should ensure it completes before
+ // shutdown:
+ else if (Preferences::sPreferences->mAreWaitingForOMTSave) {
+ Preferences::sPreferences->mClearShutdownBlockerWhenOMTSaveIsDone = true;
+ // Have to check again so we don't race.
+ if (Preferences::sPreferences->mAreWaitingForOMTSave) {
+ return NS_OK;
+ }
+ }
+ }
+
+ shutdownPhase->RemoveBlocker(this);
+ return NS_OK;
+}
+
} // namespace mozilla
#undef ENSURE_MAIN_PROCESS
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -8,20 +8,22 @@
#ifndef MOZILLA_INTERNAL_API
#error "This header is only usable from within libxul (MOZILLA_INTERNAL_API)."
#endif
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIPrefBranchInternal.h"
+#include "nsIAsyncShutdown.h"
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
+#include "nsString.h"
#include "nsWeakReference.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Monitor.h"
class nsIFile;
class nsAdoptingString;
class nsAdoptingCString;
@@ -33,16 +35,17 @@ typedef void (*PrefChangedFunc)(const ch
namespace mozilla {
namespace dom {
class PrefSetting;
} // namespace dom
class PreferenceRunnableSaver;
class PreferenceRunnableSaveDone;
+class PrefShutdownBlocker;
class Preferences final : public nsIPrefService,
public nsIObserver,
public nsIPrefBranchInternal,
public nsSupportsWeakReference
{
public:
typedef mozilla::dom::PrefSetting PrefSetting;
@@ -377,26 +380,55 @@ protected:
private:
nsCOMPtr<nsIFile> mCurrentFile;
nsCOMPtr<nsITimer> mCurrentOMTSaveTimer;
RefPtr<PreferenceRunnableSaver> mCurrentOMTSaver;
bool mAreWaitingForOMTSave;
mozilla::Monitor mOMTSaveMonitor;
+ nsCOMPtr<nsIAsyncShutdownBlocker> mShutdownBlocker;
+ bool mClearShutdownBlockerWhenOMTSaveIsDone;
static Preferences* sPreferences;
static nsIPrefBranch* sRootBranch;
static nsIPrefBranch* sDefaultRootBranch;
static bool sShutdown;
/**
* Init static members. TRUE if it succeeded. Otherwise, FALSE.
*/
static bool InitStaticMembers();
friend class PreferenceRunnableSaver;
friend class PreferenceRunnableSaveDone;
+ friend class PrefShutdownBlocker;
+};
+
+class PrefShutdownBlocker final : public nsIAsyncShutdownBlocker
+{
+public:
+ PrefShutdownBlocker(const nsString& aName) : mName(aName) {}
+
+ NS_IMETHOD
+ BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override;
+
+ NS_IMETHOD GetName(nsAString& aName) override
+ {
+ aName = mName;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetState(nsIPropertyBag**) override
+ {
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS
+protected:
+ virtual ~PrefShutdownBlocker() {}
+private:
+ const nsString mName;
};
} // namespace mozilla
#endif // mozilla_Preferences_h