Bug 789945 - part 2: add asyncshutdown blocker, r?bsmedberg draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 15 Jan 2016 20:01:12 +0000
changeset 322136 8386d7ac2802ba3105b098fcb97911ce27067205
parent 322135 ac2de7237fcbb2cd283ac52491d4079c46b941b0
child 513035 93a3ac47b3774466da70e0a28c98ca78917a4380
push id9532
push usergijskruitbosch@gmail.com
push dateFri, 15 Jan 2016 20:02:20 +0000
reviewersbsmedberg
bugs789945
milestone46.0a1
Bug 789945 - part 2: add asyncshutdown blocker, r?bsmedberg
modules/libpref/Preferences.cpp
modules/libpref/Preferences.h
--- 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