Bug 1261491 - Intermittent test_quotaExceeded_recovery.js | application crashed [@ mozilla::dom::IndexedDatabaseManager::Notify]; r=khuey
authorJan Varga <jan.varga@gmail.com>
Mon, 18 Jul 2016 18:51:54 +0200
changeset 305420 10d9d28579dc18269f663f7cad4f1d8f4b9cd141
parent 305419 048a09249037efad7c9cdb8a985c760c83bd0f71
child 305421 ac56ced6aba28b8977627a1d5d003159a067b64b
push id79566
push userjvarga@mozilla.com
push dateMon, 18 Jul 2016 16:52:46 +0000
treeherdermozilla-inbound@10d9d28579dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1261491
milestone50.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 1261491 - Intermittent test_quotaExceeded_recovery.js | application crashed [@ mozilla::dom::IndexedDatabaseManager::Notify]; r=khuey
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/quota/ActorsParent.cpp
dom/quota/Client.h
dom/quota/QuotaManager.h
dom/quota/QuotaManagerService.cpp
dom/quota/QuotaManagerService.h
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8990,16 +8990,22 @@ public:
   StartIdleMaintenance() override;
 
   virtual void
   StopIdleMaintenance() override;
 
   virtual void
   ShutdownWorkThreads() override;
 
+  virtual void
+  DidInitialize(QuotaManager* aQuotaManager) override;
+
+  virtual void
+  WillShutdown() override;
+
 private:
   ~QuotaClient();
 
   nsresult
   GetDirectory(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                nsIFile** aDirectory);
 
@@ -17408,16 +17414,36 @@ QuotaClient::ShutdownWorkThreads()
     gFileHandleThreadPool.get();
   if (fileHandleThreadPool) {
     fileHandleThreadPool->Shutdown();
 
     gFileHandleThreadPool = nullptr;
   }
 }
 
+void
+QuotaClient::DidInitialize(QuotaManager* aQuotaManager)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
+    mgr->NoteLiveQuotaManager(aQuotaManager);
+  }
+}
+
+void
+QuotaClient::WillShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
+    mgr->NoteShuttingDownQuotaManager();
+  }
+}
+
 nsresult
 QuotaClient::GetDirectory(PersistenceType aPersistenceType,
                           const nsACString& aOrigin, nsIFile** aDirectory)
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
@@ -19925,24 +19951,22 @@ FactoryOp::Open()
 
   if (permission == PermissionRequestBase::kPermissionDenied) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   {
     // These services have to be started on the main thread currently.
 
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
-    if (NS_WARN_IF(!mgr)) {
+    IndexedDatabaseManager* mgr;
+    if (NS_WARN_IF(!(mgr = IndexedDatabaseManager::GetOrCreate()))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    mgr->NoteBackgroundThread(mOwningThread);
-
     nsCOMPtr<mozIStorageService> ss;
     if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
 
   const DatabaseMetadata& metadata = mCommonParams.metadata();
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -343,16 +343,20 @@ IndexedDatabaseManager::Init()
     NS_ENSURE_STATE(obs);
 
     nsresult rv =
       obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mDeleteTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     NS_ENSURE_STATE(mDeleteTimer);
+
+    if (QuotaManager* quotaManager = QuotaManager::Get()) {
+      NoteLiveQuotaManager(quotaManager);
+    }
   }
 
   Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                        kTestingPref,
                                        &gTestingMode);
   Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                        kPrefExperimental,
                                        &gExperimentalFeaturesEnabled);
@@ -742,23 +746,34 @@ void
 IndexedDatabaseManager::ClearBackgroundActor()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mBackgroundActor = nullptr;
 }
 
 void
-IndexedDatabaseManager::NoteBackgroundThread(nsIEventTarget* aBackgroundThread)
+IndexedDatabaseManager::NoteLiveQuotaManager(QuotaManager* aQuotaManager)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aBackgroundThread);
+  MOZ_ASSERT(aQuotaManager);
+
+  mBackgroundThread = aQuotaManager->OwningThread();
+}
 
-  mBackgroundThread = aBackgroundThread;
+void
+IndexedDatabaseManager::NoteShuttingDownQuotaManager()
+{
+  MOZ_ASSERT(IsMainProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
+
+  mBackgroundThread = nullptr;
 }
 
 already_AddRefed<FileManager>
 IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
                                        const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
@@ -847,16 +862,20 @@ IndexedDatabaseManager::AsyncDeleteFile(
                                         int64_t aFileId)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aFileManager);
   MOZ_ASSERT(aFileId > 0);
   MOZ_ASSERT(mDeleteTimer);
 
+  if (!mBackgroundThread) {
+    return NS_OK;
+  }
+
   nsresult rv = mDeleteTimer->Cancel();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs,
                                       nsITimer::TYPE_ONE_SHOT);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1038,16 +1057,17 @@ IndexedDatabaseManager::Observe(nsISuppo
    return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Notify(nsITimer* aTimer)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBackgroundThread);
 
   for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) {
     auto key = iter.Key();
     auto value = iter.Data();
     MOZ_ASSERT(!value->IsEmpty());
 
     RefPtr<DeleteFilesRunnable> runnable =
       new DeleteFilesRunnable(mBackgroundThread, key, *value);
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -23,29 +23,36 @@ class nsIEventTarget;
 namespace mozilla {
 
 class EventChainPostVisitor;
 
 namespace dom {
 
 class IDBFactory;
 
+namespace quota {
+
+class QuotaManager;
+
+} // namespace quota
+
 namespace indexedDB {
 
 class BackgroundUtilsChild;
 class FileManager;
 class FileManagerInfo;
 
 } // namespace indexedDB
 
 class IndexedDatabaseManager final
   : public nsIObserver
   , public nsITimerCallback
 {
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
+  typedef mozilla::dom::quota::QuotaManager QuotaManager;
   typedef mozilla::dom::indexedDB::FileManager FileManager;
   typedef mozilla::dom::indexedDB::FileManagerInfo FileManagerInfo;
 
 public:
   enum LoggingMode
   {
     Logging_Disabled = 0,
     Logging_Concise,
@@ -123,17 +130,20 @@ public:
 
   static bool
   IsFileHandleEnabled();
 
   void
   ClearBackgroundActor();
 
   void
-  NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
+  NoteLiveQuotaManager(QuotaManager* aQuotaManager);
+
+  void
+  NoteShuttingDownQuotaManager();
 
   already_AddRefed<FileManager>
   GetFileManager(PersistenceType aPersistenceType,
                  const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -2279,16 +2279,20 @@ CreateRunnable::RegisterObserver()
 
   QuotaManagerService* qms = QuotaManagerService::GetOrCreate();
   if (NS_WARN_IF(!qms)) {
     return rv;
   }
 
   qms->NoteLiveManager(mManager);
 
+  for (RefPtr<Client>& client : mManager->mClients) {
+    client->DidInitialize(mManager);
+  }
+
   return NS_OK;
 }
 
 void
 QuotaManager::
 CreateRunnable::CallCallbacks()
 {
   AssertIsOnOwningThread();
@@ -2382,21 +2386,16 @@ CreateRunnable::Run()
   return NS_OK;
 }
 
 NS_IMETHODIMP
 QuotaManager::
 ShutdownRunnable::Run()
 {
   if (NS_IsMainThread()) {
-    QuotaManagerService* qms = QuotaManagerService::Get();
-    MOZ_ASSERT(qms);
-
-    qms->NoteFinishedManager();
-
     mDone = true;
 
     return NS_OK;
   }
 
   AssertIsOnBackgroundThread();
 
   RefPtr<QuotaManager> quotaManager = gInstance.get();
@@ -2416,16 +2415,26 @@ NS_IMPL_ISUPPORTS(QuotaManager::Shutdown
 NS_IMETHODIMP
 QuotaManager::
 ShutdownObserver::Observe(nsISupports* aSubject,
                           const char* aTopic,
                           const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID));
+  MOZ_ASSERT(gInstance);
+
+  QuotaManagerService* qms = QuotaManagerService::Get();
+  MOZ_ASSERT(qms);
+
+  qms->NoteShuttingDownManager();
+
+  for (RefPtr<Client>& client : gInstance->mClients) {
+    client->WillShutdown();
+  }
 
   bool done = false;
 
   RefPtr<ShutdownRunnable> shutdownRunnable = new ShutdownRunnable(done);
   MOZ_ALWAYS_SUCCEEDS(
     mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
 
   nsIThread* currentThread = NS_GetCurrentThread();
@@ -2746,18 +2755,16 @@ QuotaManager::GetOrCreate(nsIRunnable* a
     gCreateRunnable->AddCallback(aCallback);
   }
 }
 
 // static
 QuotaManager*
 QuotaManager::Get()
 {
-  MOZ_ASSERT(!NS_IsMainThread());
-
   // Does not return an owning reference.
   return gInstance;
 }
 
 // static
 bool
 QuotaManager::IsShuttingDown()
 {
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -16,16 +16,17 @@
 class nsIRunnable;
 
 #define IDB_DIRECTORY_NAME "idb"
 #define ASMJSCACHE_DIRECTORY_NAME "asmjs"
 #define DOMCACHE_DIRECTORY_NAME "cache"
 
 BEGIN_QUOTA_NAMESPACE
 
+class QuotaManager;
 class UsageInfo;
 
 // An abstract interface for quota manager clients.
 // Each storage API must provide an implementation of this interface in order
 // to participate in centralized quota and storage handling.
 class Client
 {
 public:
@@ -122,16 +123,25 @@ public:
   StartIdleMaintenance() = 0;
 
   virtual void
   StopIdleMaintenance() = 0;
 
   virtual void
   ShutdownWorkThreads() = 0;
 
+  // Methods which are called on the main thread.
+  virtual void
+  DidInitialize(QuotaManager* aQuotaManager)
+  { }
+
+  virtual void
+  WillShutdown()
+  { }
+
 protected:
   virtual ~Client()
   { }
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_client_h__
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -508,16 +508,18 @@ private:
 
   // A timer that gets activated at shutdown to ensure we close all storages.
   nsCOMPtr<nsITimer> mShutdownTimer;
 
   // A list of all successfully initialized origins. This list isn't protected
   // by any mutex but it is only ever touched on the IO thread.
   nsTArray<nsCString> mInitializedOrigins;
 
+  // This array is populated at initialization time and then never modified, so
+  // it can be iterated on any thread.
   AutoTArray<RefPtr<Client>, Client::TYPE_MAX> mClients;
 
   nsString mBasePath;
   nsString mIndexedDBPath;
   nsString mStoragePath;
   nsString mPermanentStoragePath;
   nsString mTemporaryStoragePath;
   nsString mDefaultStoragePath;
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -252,17 +252,17 @@ QuotaManagerService::NoteLiveManager(Quo
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
 
   mBackgroundThread = aManager->OwningThread();
 }
 
 void
-QuotaManagerService::NoteFinishedManager()
+QuotaManagerService::NoteShuttingDownManager()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
 
   mBackgroundThread = nullptr;
 }
 
 void
--- a/dom/quota/QuotaManagerService.h
+++ b/dom/quota/QuotaManagerService.h
@@ -64,17 +64,17 @@ public:
 
   void
   ClearBackgroundActor();
 
   void
   NoteLiveManager(QuotaManager* aManager);
 
   void
-  NoteFinishedManager();
+  NoteShuttingDownManager();
 
   // Called when a process is being shot down. Aborts any running operations
   // for the given process.
   void
   AbortOperationsForProcess(ContentParentId aContentParentId);
 
 private:
   QuotaManagerService();