Bug 1286798 - Part 28: Add more QuotaClient::IsShuttingDownOnBackgroundThread() and MayProceed() checks; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:48:44 +0100
changeset 508026 17a4f3ac425a1c687b5d238452a4a2740f301907
parent 508025 e75a175ddf880c7d3f0434cda155a0d57876f85b
child 508027 70ba8b2410f833ec27655fb3cb6f69f7bb01fbcb
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1286798
milestone65.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 1286798 - Part 28: Add more QuotaClient::IsShuttingDownOnBackgroundThread() and MayProceed() checks; r=asuth The shutdown and actor destroyed flag is now checked after each dispatch.
dom/localstorage/ActorsParent.cpp
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -1600,28 +1600,34 @@ private:
 
   nsresult
   Open() override;
 
   nsresult
   CheckExistingOperations();
 
   nsresult
+  CheckClosingDatastoreInternal();
+
+  nsresult
   CheckClosingDatastore();
 
   nsresult
+  BeginDatastorePreparationInternal();
+
+  nsresult
   BeginDatastorePreparation();
 
   nsresult
   QuotaManagerOpen();
 
   nsresult
   OpenDirectory();
 
-  nsresult
+  void
   SendToIOThread();
 
   nsresult
   DatabaseWork();
 
   nsresult
   DatabaseNotAvailable();
 
@@ -1635,16 +1641,22 @@ private:
   VerifyDatabaseInformation(mozIStorageConnection* aConnection);
 
   already_AddRefed<QuotaObject>
   GetQuotaObject();
 
   nsresult
   BeginLoadData();
 
+  void
+  FinishNesting();
+
+  nsresult
+  FinishNestingOnNonOwningThread();
+
   nsresult
   NestedRun() override;
 
   void
   GetResponse(LSRequestResponse& aResponse) override;
 
   void
   Cleanup() override;
@@ -3874,38 +3886,44 @@ LSRequestBase::NestedRun()
 }
 
 void
 LSRequestBase::SendReadyMessage()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::SendingReadyMessage);
 
-  if (!MayProceed()) {
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      !MayProceed()) {
     MaybeSetFailureCode(NS_ERROR_FAILURE);
-
+  }
+
+  if (MayProceed()) {
+    Unused << SendReady();
+
+    mState = State::WaitingForFinish;
+  } else {
     Cleanup();
 
     mState = State::Completed;
-  } else {
-    Unused << SendReady();
-
-    mState = State::WaitingForFinish;
   }
 }
 
 void
 LSRequestBase::SendResults()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::SendingResults);
 
-  if (!MayProceed()) {
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      !MayProceed()) {
     MaybeSetFailureCode(NS_ERROR_FAILURE);
-  } else {
+  }
+
+  if (MayProceed()) {
     LSRequestResponse response;
 
     if (NS_SUCCEEDED(ResultCode())) {
       GetResponse(response);
     } else {
       response = ResultCode();
     }
 
@@ -3985,17 +4003,22 @@ LSRequestBase::RecvCancel()
 
 mozilla::ipc::IPCResult
 LSRequestBase::RecvFinish()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::WaitingForFinish);
 
   mState = State::SendingResults;
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
+
+  // This LSRequestBase can only be held alive by the IPDL. Run() can end up
+  // with clearing that last reference. So we need to add a self reference here.
+  RefPtr<LSRequestBase> kungFuDeathGrip = this;
+
+  MOZ_ALWAYS_SUCCEEDS(this->Run());
 
   return IPC_OK();
 }
 
 /*******************************************************************************
  * PrepareDatastoreOp
  ******************************************************************************/
 
@@ -4130,17 +4153,17 @@ PrepareDatastoreOp::CheckExistingOperati
       // Only one op can be delayed.
       MOZ_ASSERT(!existingOp->mDelayedOp);
       existingOp->mDelayedOp = this;
 
       return NS_OK;
     }
   }
 
-  nsresult rv = CheckClosingDatastore();
+  nsresult rv = CheckClosingDatastoreInternal();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -4150,51 +4173,87 @@ PrepareDatastoreOp::CheckClosingDatastor
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
       !MayProceed()) {
     return NS_ERROR_FAILURE;
   }
 
+  nsresult rv = CheckClosingDatastoreInternal();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+PrepareDatastoreOp::CheckClosingDatastoreInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::Nesting);
+  MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(MayProceed());
+
   mNestedState = NestedState::PreparationPending;
 
   RefPtr<Datastore> datastore;
   if (gDatastores &&
       (datastore = gDatastores->Get(mOrigin)) &&
       datastore->IsClosed()) {
     datastore->WaitForConnectionToComplete(this);
 
     return NS_OK;
   }
 
-  nsresult rv = BeginDatastorePreparation();
+  nsresult rv = BeginDatastorePreparationInternal();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 PrepareDatastoreOp::BeginDatastorePreparation()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
 
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      !MayProceed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = BeginDatastorePreparationInternal();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+PrepareDatastoreOp::BeginDatastorePreparationInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::Nesting);
+  MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(MayProceed());
+
   if (gDatastores && (mDatastore = gDatastores->Get(mOrigin))) {
     MOZ_ASSERT(!mDatastore->IsClosed());
 
     mDatastore->NoteLivePrepareDatastoreOp(this);
 
-    mState = State::SendingReadyMessage;
-    mNestedState = NestedState::AfterNesting;
-
-    Unused << this->Run();
+    FinishNesting();
 
     return NS_OK;
   }
 
   if (QuotaManager::Get()) {
     nsresult rv = OpenDirectory();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -4238,73 +4297,64 @@ PrepareDatastoreOp::OpenDirectory()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::PreparationPending ||
              mNestedState == NestedState::QuotaManagerPending);
   MOZ_ASSERT(!mOrigin.IsEmpty());
   MOZ_ASSERT(!mDirectoryLock);
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(MayProceed());
   MOZ_ASSERT(QuotaManager::Get());
 
   mNestedState = NestedState::DirectoryOpenPending;
   QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
                                      mGroup,
                                      mOrigin,
                                      mozilla::dom::quota::Client::LS,
                                      /* aExclusive */ false,
                                      this);
 
   mRequestedDirectoryLock = true;
 
   return NS_OK;
 }
 
-nsresult
+void
 PrepareDatastoreOp::SendToIOThread()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
-
-  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
-      !MayProceed()) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(MayProceed());
 
   // Skip all disk related stuff and transition to SendingReadyMessage if we
   // are preparing a datastore for private browsing.
   // Note that we do use a directory lock for private browsing even though we
   // don't do any stuff on disk. The thing is that without a directory lock,
   // quota manager wouldn't call AbortOperations for our private browsing
   // origin when a clear origin operation is requested. AbortOperations
   // requests all databases to close and the datastore is destroyed in the end.
   // Any following LocalStorage API call will trigger preparation of a new
   // (empty) datastore.
   if (mPrivateBrowsingId) {
-    mState = State::SendingReadyMessage;
-    mNestedState = NestedState::AfterNesting;
-
-    Unused << this->Run();
-
-    return NS_OK;
+    FinishNesting();
+
+    return;
   }
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   // Must set this before dispatching otherwise we will race with the IO thread.
   mNestedState = NestedState::DatabaseWorkOpen;
 
-  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
+  MOZ_ALWAYS_SUCCEEDS(
+    quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
 }
 
 nsresult
 PrepareDatastoreOp::DatabaseWork()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mArchivedOriginInfo);
   MOZ_ASSERT(mState == State::Nesting);
@@ -4512,22 +4562,17 @@ nsresult
 PrepareDatastoreOp::DatabaseNotAvailable()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::DatabaseWorkOpen);
 
   mDatabaseNotAvailable = true;
 
-  // Must set this before dispatching otherwise we will race with the owning
-  // thread.
-  mState = State::SendingReadyMessage;
-  mNestedState = NestedState::AfterNesting;
-
-  nsresult rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
+  nsresult rv = FinishNestingOnNonOwningThread();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -4667,16 +4712,50 @@ PrepareDatastoreOp::BeginLoadData()
 
   // This is cleared in LoadDataOp::Cleanup() before the load data op is
   // destroyed.
   mLoadDataOp = loadDataOp;
 
   return NS_OK;
 }
 
+void
+PrepareDatastoreOp::FinishNesting()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::Nesting);
+
+  // The caller holds a strong reference to us, no need for a self reference
+  // before calling Run().
+
+  mState = State::SendingReadyMessage;
+  mNestedState = NestedState::AfterNesting;
+
+  MOZ_ALWAYS_SUCCEEDS(Run());
+}
+
+nsresult
+PrepareDatastoreOp::FinishNestingOnNonOwningThread()
+{
+  MOZ_ASSERT(!IsOnOwningThread());
+  MOZ_ASSERT(mState == State::Nesting);
+
+  // Must set mState before dispatching otherwise we will race with the owning
+  // thread.
+  mState = State::SendingReadyMessage;
+  mNestedState = NestedState::AfterNesting;
+
+  nsresult rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 nsresult
 PrepareDatastoreOp::NestedRun()
 {
   nsresult rv;
 
   switch (mNestedState) {
     case NestedState::CheckExistingOperations:
       rv = CheckExistingOperations();
@@ -4887,51 +4966,41 @@ PrepareDatastoreOp::ActorDestroy(ActorDe
 void
 PrepareDatastoreOp::DirectoryLockAcquired(DirectoryLock* aLock)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
   MOZ_ASSERT(!mDirectoryLock);
 
-  mDirectoryLock = aLock;
-
-  nsresult rv = SendToIOThread();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    MaybeSetFailureCode(rv);
-
-    // The caller holds a strong reference to us, no need for a self reference
-    // before calling Run().
-
-    mState = State::SendingReadyMessage;
-    mNestedState = NestedState::AfterNesting;
-
-    MOZ_ALWAYS_SUCCEEDS(Run());
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      !MayProceed()) {
+    MaybeSetFailureCode(NS_ERROR_FAILURE);
+
+    FinishNesting();
 
     return;
   }
+
+  mDirectoryLock = aLock;
+
+  SendToIOThread();
 }
 
 void
 PrepareDatastoreOp::DirectoryLockFailed()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Nesting);
   MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
   MOZ_ASSERT(!mDirectoryLock);
 
   MaybeSetFailureCode(NS_ERROR_FAILURE);
 
-  // The caller holds a strong reference to us, no need for a self reference
-  // before calling Run().
-
-  mState = State::SendingReadyMessage;
-  mNestedState = NestedState::AfterNesting;
-
-  MOZ_ALWAYS_SUCCEEDS(Run());
+  FinishNesting();
 }
 
 nsresult
 PrepareDatastoreOp::
 LoadDataOp::DoDatastoreWork()
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
@@ -4986,38 +5055,33 @@ LoadDataOp::OnSuccess()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mPrepareDatastoreOp);
   MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
   MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
                NestedState::DatabaseWorkLoadData);
   MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
 
-  mPrepareDatastoreOp->mState = State::SendingReadyMessage;
-  mPrepareDatastoreOp->mNestedState = NestedState::AfterNesting;
-
-  MOZ_ALWAYS_SUCCEEDS(mPrepareDatastoreOp->Run());
+  mPrepareDatastoreOp->FinishNesting();
 }
 
 void
 PrepareDatastoreOp::
 LoadDataOp::OnFailure(nsresult aResultCode)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mPrepareDatastoreOp);
   MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
   MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
                NestedState::DatabaseWorkLoadData);
   MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
 
   mPrepareDatastoreOp->SetFailureCode(aResultCode);
-  mPrepareDatastoreOp->mState = State::SendingReadyMessage;
-  mPrepareDatastoreOp->mNestedState = NestedState::AfterNesting;
-
-  MOZ_ALWAYS_SUCCEEDS(mPrepareDatastoreOp->Run());
+
+  mPrepareDatastoreOp->FinishNesting();
 }
 
 void
 PrepareDatastoreOp::
 LoadDataOp::Cleanup()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mPrepareDatastoreOp);
@@ -5130,19 +5194,22 @@ LSSimpleRequestBase::Dispatch()
 }
 
 void
 LSSimpleRequestBase::SendResults()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::SendingResults);
 
-  if (!MayProceed()) {
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      !MayProceed()) {
     MaybeSetFailureCode(NS_ERROR_FAILURE);
-  } else {
+  }
+
+  if (MayProceed()) {
     LSSimpleRequestResponse response;
 
     if (NS_SUCCEEDED(ResultCode())) {
       GetResponse(response);
     } else {
       response = ResultCode();
     }