diff --git a/dom/cache/Context.cpp b/dom/cache/Context.cpp index e1d233735d1b..89f25048af99 100644 --- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -230,14 +230,16 @@ void Context::QuotaInitRunnable::OpenDirectory() { mState == STATE_OPEN_DIRECTORY); MOZ_DIAGNOSTIC_ASSERT(QuotaManager::Get()); - // QuotaManager::OpenDirectory() will hold a reference to us as - // a listener. We will then get DirectoryLockAcquired() on the owning - // thread when it is safe to access our storage directory. + RefPtr directoryLock = + QuotaManager::Get()->CreateDirectoryLock( + PERSISTENCE_TYPE_DEFAULT, mQuotaInfo, quota::Client::DOMCACHE, + /* aExclusive */ false); + + // DirectoryLock::Acquire() will hold a reference to us as a listener. We will + // then get DirectoryLockAcquired() on the owning thread when it is safe to + // access our storage directory. mState = STATE_WAIT_FOR_DIRECTORY_LOCK; - RefPtr pendingDirectoryLock = - QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT, mQuotaInfo, - quota::Client::DOMCACHE, - /* aExclusive */ false, this); + directoryLock->Acquire(this); } void Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock) { diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index ed1f44debb04..d27371a13aea 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -13214,11 +13214,12 @@ void DeleteFilesRunnable::Open() { return; } - mState = State_DirectoryOpenPending; - - RefPtr pendingDirectoryLock = quotaManager->OpenDirectory( + RefPtr directoryLock = quotaManager->CreateDirectoryLock( mFileManager->Type(), mFileManager->GroupAndOrigin(), quota::Client::IDB, - /* aExclusive */ false, this); + /* aExclusive */ false); + + mState = State_DirectoryOpenPending; + directoryLock->Acquire(this); } void DeleteFilesRunnable::DoDatabaseWork() { @@ -13413,12 +13414,14 @@ nsresult Maintenance::OpenDirectory() { // Get a shared lock for /storage/*/*/idb - mState = State::DirectoryOpenPending; - RefPtr pendingDirectoryLock = - QuotaManager::Get()->OpenDirectoryInternal( + RefPtr directoryLock = + QuotaManager::Get()->CreateDirectoryLockInternal( Nullable(), OriginScope::FromNull(), Nullable(Client::IDB), - /* aExclusive */ false, this); + /* aExclusive */ false); + + mState = State::DirectoryOpenPending; + directoryLock->Acquire(this); return NS_OK; } @@ -15714,11 +15717,13 @@ nsresult FactoryOp::OpenDirectory() { IDB_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(nsString, dbFile, GetPath)); }())); + RefPtr directoryLock = quotaManager->CreateDirectoryLock( + persistenceType, mQuotaInfo, Client::IDB, + /* aExclusive */ false); + mState = State::DirectoryOpenPending; - RefPtr pendingDirectoryLock = - quotaManager->OpenDirectory(persistenceType, mQuotaInfo, Client::IDB, - /* aExclusive */ false, this); + directoryLock->Acquire(this); return NS_OK; } diff --git a/dom/localstorage/ActorsParent.cpp b/dom/localstorage/ActorsParent.cpp index 25e8f0676e7b..0d41d76294c6 100644 --- a/dom/localstorage/ActorsParent.cpp +++ b/dom/localstorage/ActorsParent.cpp @@ -6685,10 +6685,18 @@ nsresult PrepareDatastoreOp::OpenDirectory() { MOZ_ASSERT(MayProceed()); MOZ_ASSERT(QuotaManager::Get()); - mNestedState = NestedState::DirectoryOpenPending; - mPendingDirectoryLock = QuotaManager::Get()->OpenDirectory( + mPendingDirectoryLock = QuotaManager::Get()->CreateDirectoryLock( PERSISTENCE_TYPE_DEFAULT, mQuotaInfo, mozilla::dom::quota::Client::LS, - /* aExclusive */ false, this); + /* aExclusive */ false); + + mNestedState = NestedState::DirectoryOpenPending; + + { + // Pin the directory lock, because Acquire might clear mPendingDirectoryLock + // during the Acquire call. + RefPtr pinnedDirectoryLock = mPendingDirectoryLock; + pinnedDirectoryLock->Acquire(this); + } return NS_OK; } diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 94552e5e602e..928b9b535044 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -666,8 +666,7 @@ class DirectoryLockImpl final : public DirectoryLock { const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, bool aExclusive, bool aInternal, - ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag, - RefPtr aOpenListener); + ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag); void AssertIsOnOwningThread() const #ifdef DEBUG @@ -777,6 +776,10 @@ class DirectoryLockImpl final : public DirectoryLock { return mClientType.Value(); } + void Acquire(RefPtr aOpenListener); + + void Acquire(); + already_AddRefed Specialize( PersistenceType aPersistenceType, const quota::GroupAndOrigin& aGroupAndOrigin, @@ -788,13 +791,19 @@ class DirectoryLockImpl final : public DirectoryLock { ~DirectoryLockImpl(); }; -const DirectoryLockImpl* GetDirectoryLockImpl( +const DirectoryLockImpl* AsDirectoryLockImpl( const DirectoryLock* aDirectoryLock) { MOZ_ASSERT(aDirectoryLock); return static_cast(aDirectoryLock); } +DirectoryLockImpl* AsDirectoryLockImpl(DirectoryLock* aDirectoryLock) { + MOZ_ASSERT(aDirectoryLock); + + return static_cast(aDirectoryLock); +} + class QuotaManager::Observer final : public nsIObserver { static Observer* sInstance; @@ -2695,41 +2704,44 @@ bool RecvShutdownQuotaManager() { * Directory lock ******************************************************************************/ -int64_t DirectoryLock::Id() const { return GetDirectoryLockImpl(this)->Id(); } +int64_t DirectoryLock::Id() const { return AsDirectoryLockImpl(this)->Id(); } PersistenceType DirectoryLock::GetPersistenceType() const { - return GetDirectoryLockImpl(this)->GetPersistenceType(); + return AsDirectoryLockImpl(this)->GetPersistenceType(); } quota::GroupAndOrigin DirectoryLock::GroupAndOrigin() const { - return GetDirectoryLockImpl(this)->GroupAndOrigin(); + return AsDirectoryLockImpl(this)->GroupAndOrigin(); } const nsACString& DirectoryLock::Origin() const { - return GetDirectoryLockImpl(this)->Origin(); + return AsDirectoryLockImpl(this)->Origin(); } Client::Type DirectoryLock::ClientType() const { - return GetDirectoryLockImpl(this)->ClientType(); + return AsDirectoryLockImpl(this)->ClientType(); +} + +void DirectoryLock::Acquire(RefPtr aOpenListener) { + AsDirectoryLockImpl(this)->Acquire(std::move(aOpenListener)); } already_AddRefed DirectoryLock::Specialize( PersistenceType aPersistenceType, const quota::GroupAndOrigin& aGroupAndOrigin, Client::Type aClientType) const { - return GetDirectoryLockImpl(this)->Specialize(aPersistenceType, - aGroupAndOrigin, aClientType); + return AsDirectoryLockImpl(this)->Specialize(aPersistenceType, + aGroupAndOrigin, aClientType); } -void DirectoryLock::Log() const { GetDirectoryLockImpl(this)->Log(); } +void DirectoryLock::Log() const { AsDirectoryLockImpl(this)->Log(); } DirectoryLockImpl::DirectoryLockImpl( MovingNotNull> aQuotaManager, const int64_t aId, const Nullable& aPersistenceType, const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, const bool aExclusive, const bool aInternal, - const ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag, - RefPtr aOpenListener) + const ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag) : mQuotaManager(std::move(aQuotaManager)), mPersistenceType(aPersistenceType), mGroup(aGroup), @@ -2750,11 +2762,6 @@ DirectoryLockImpl::DirectoryLockImpl( MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin()); MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull()); MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax()); - MOZ_ASSERT_IF(!aInternal, aOpenListener); - - if (aOpenListener) { - mOpenListener.init(WrapNotNullUnchecked(std::move(aOpenListener))); - } } DirectoryLockImpl::~DirectoryLockImpl() { @@ -2835,6 +2842,78 @@ void DirectoryLockImpl::NotifyOpenListener() { mPending.Flip(); } +void DirectoryLockImpl::Acquire(RefPtr aOpenListener) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aOpenListener); + + mOpenListener.init(WrapNotNullUnchecked(std::move(aOpenListener))); + + mQuotaManager->AddPendingDirectoryLock(*this); + + // See if this lock needs to wait. + bool blocked = false; + + // XXX It is probably unnecessary to iterate this in reverse order. + for (DirectoryLockImpl* const existingLock : + Reversed(mQuotaManager->mDirectoryLocks)) { + if (MustWaitFor(*existingLock)) { + existingLock->AddBlockingLock(*this); + AddBlockedOnLock(*existingLock); + blocked = true; + } + } + + mQuotaManager->RegisterDirectoryLock(*this); + + // Otherwise, notify the open listener immediately. + if (!blocked) { + NotifyOpenListener(); + return; + } + + if (!mExclusive || !mInternal) { + return; + } + + // All the locks that block this new exclusive internal lock need to be + // invalidated. We also need to notify clients to abort operations for them. + QuotaManager::DirectoryLockIdTableArray lockIds; + lockIds.SetLength(Client::TypeMax()); + + const auto& blockedOnLocks = GetBlockedOnLocks(); + MOZ_ASSERT(!blockedOnLocks.IsEmpty()); + + for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) { + if (!blockedOnLock->IsInternal()) { + blockedOnLock->Invalidate(); + + // Clients don't have to handle pending locks. Invalidation is sufficient + // in that case (once a lock is ready and the listener needs to be + // notified, we will call DirectoryLockFailed instead of + // DirectoryLockAcquired which should release any remaining references to + // the lock). + if (!blockedOnLock->IsPending()) { + lockIds[blockedOnLock->ClientType()].Put(blockedOnLock->Id()); + } + } + } + + mQuotaManager->AbortOperationsForLocks(lockIds); +} + +void DirectoryLockImpl::Acquire() { + AssertIsOnOwningThread(); + +#ifdef DEBUG + for (const DirectoryLockImpl* const existingLock : + mQuotaManager->mDirectoryLocks) { + MOZ_ASSERT(!MustWaitFor(*existingLock)); + } +#endif + + mQuotaManager->RegisterDirectoryLock(*this); +} + already_AddRefed DirectoryLockImpl::Specialize( PersistenceType aPersistenceType, const quota::GroupAndOrigin& aGroupAndOrigin, @@ -2856,16 +2935,14 @@ already_AddRefed DirectoryLockImpl::Specialize( Nullable(aPersistenceType), aGroupAndOrigin.mGroup, OriginScope::FromOrigin(aGroupAndOrigin.mOrigin), Nullable(aClientType), - /* aExclusive */ false, mInternal, ShouldUpdateLockIdTableFlag::Yes, - /* aOpenListener */ nullptr); + /* aExclusive */ false, mInternal, ShouldUpdateLockIdTableFlag::Yes); if (NS_WARN_IF(!Overlaps(*lock))) { return nullptr; } #ifdef DEBUG - for (uint32_t index = mQuotaManager->mDirectoryLocks.Length(); index > 0; - index--) { - DirectoryLockImpl* existingLock = mQuotaManager->mDirectoryLocks[index - 1]; + for (DirectoryLockImpl* const existingLock : + Reversed(mQuotaManager->mDirectoryLocks)) { if (existingLock != this && !existingLock->MustWaitFor(*this)) { MOZ_ASSERT(!existingLock->MustWaitFor(*lock)); } @@ -3585,9 +3662,7 @@ bool QuotaManager::IsDotFile(const nsAString& aFileName) { auto QuotaManager::CreateDirectoryLock( const Nullable& aPersistenceType, const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, - bool aExclusive, bool aInternal, - RefPtr aOpenListener, bool& aBlockedOut) - -> already_AddRefed { + bool aExclusive, bool aInternal) -> already_AddRefed { AssertIsOnOwningThread(); MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty()); MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull()); @@ -3597,35 +3672,12 @@ auto QuotaManager::CreateDirectoryLock( MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin()); MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull()); MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax()); - MOZ_ASSERT_IF(!aInternal, aOpenListener); RefPtr lock = new DirectoryLockImpl( WrapNotNullUnchecked(this), GenerateDirectoryLockId(), aPersistenceType, aGroup, aOriginScope, aClientType, aExclusive, aInternal, - ShouldUpdateLockIdTableFlag::Yes, std::move(aOpenListener)); + ShouldUpdateLockIdTableFlag::Yes); - mPendingDirectoryLocks.AppendElement(lock); - - // See if this lock needs to wait. - bool blocked = false; - - // XXX It is probably unnecessary to iterate this in reverse order. - for (DirectoryLockImpl* const existingLock : Reversed(mDirectoryLocks)) { - if (lock->MustWaitFor(*existingLock)) { - existingLock->AddBlockingLock(*lock); - lock->AddBlockedOnLock(*existingLock); - blocked = true; - } - } - - RegisterDirectoryLock(*lock); - - // Otherwise, notify the open listener immediately. - if (!blocked) { - lock->NotifyOpenListener(); - } - - aBlockedOut = blocked; return lock.forget(); } @@ -3642,15 +3694,7 @@ auto QuotaManager::CreateDirectoryLockForEviction( OriginScope::FromOrigin(aGroupAndOrigin.mOrigin), Nullable(), /* aExclusive */ true, /* aInternal */ true, - ShouldUpdateLockIdTableFlag::No, nullptr); - -#ifdef DEBUG - for (const DirectoryLockImpl* const existingLock : mDirectoryLocks) { - MOZ_ASSERT(!lock->MustWaitFor(*existingLock)); - } -#endif - - RegisterDirectoryLock(*lock); + ShouldUpdateLockIdTableFlag::No); return lock.forget(); } @@ -3724,6 +3768,12 @@ void QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl& aLock) { aLock.SetRegistered(false); } +void QuotaManager::AddPendingDirectoryLock(DirectoryLockImpl& aLock) { + AssertIsOnOwningThread(); + + mPendingDirectoryLocks.AppendElement(&aLock); +} + void QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl& aLock) { AssertIsOnOwningThread(); @@ -3845,6 +3895,9 @@ uint64_t QuotaManager::CollectOriginsForEviction( RefPtr lock = CreateDirectoryLockForEviction( originInfo->mGroupInfo->mPersistenceType, GroupAndOrigin{originInfo->mGroupInfo->mGroup, originInfo->mOrigin}); + + lock->Acquire(); + aLocks.AppendElement(lock.forget()); } @@ -4805,6 +4858,15 @@ void QuotaManager::PersistOrigin(const GroupAndOrigin& aGroupAndOrigin) { } } +void QuotaManager::AbortOperationsForLocks( + const DirectoryLockIdTableArray& aLockIds) { + for (Client::Type type : AllClientTypes()) { + if (aLockIds[type].Filled()) { + (*mClients)[type]->AbortOperationsForLocks(aLockIds[type]); + } + } +} + void QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId) { AssertIsOnOwningThread(); @@ -6312,69 +6374,32 @@ nsresult QuotaManager::EnsureStorageIsInitialized() { return NS_OK; } -already_AddRefed QuotaManager::OpenDirectory( +already_AddRefed QuotaManager::CreateDirectoryLock( PersistenceType aPersistenceType, const GroupAndOrigin& aGroupAndOrigin, - Client::Type aClientType, bool aExclusive, - RefPtr aOpenListener) { + Client::Type aClientType, bool aExclusive) { AssertIsOnOwningThread(); - bool blocked; RefPtr lock = CreateDirectoryLock( Nullable(aPersistenceType), aGroupAndOrigin.mGroup, OriginScope::FromOrigin(aGroupAndOrigin.mOrigin), - Nullable(aClientType), aExclusive, false, - std::move(aOpenListener), blocked); + Nullable(aClientType), aExclusive, false); MOZ_ASSERT(lock); - return blocked ? lock.forget() : nullptr; + return lock.forget(); } -already_AddRefed QuotaManager::OpenDirectoryInternal( +already_AddRefed QuotaManager::CreateDirectoryLockInternal( const Nullable& aPersistenceType, const OriginScope& aOriginScope, const Nullable& aClientType, - bool aExclusive, OpenDirectoryListener* aOpenListener) { + bool aExclusive) { AssertIsOnOwningThread(); - bool blocked; - RefPtr lock = - CreateDirectoryLock(aPersistenceType, ""_ns, aOriginScope, - Nullable(aClientType), aExclusive, true, - aOpenListener, blocked); + RefPtr lock = CreateDirectoryLock( + aPersistenceType, ""_ns, aOriginScope, + Nullable(aClientType), aExclusive, true); MOZ_ASSERT(lock); - if (!aExclusive) { - return blocked ? lock.forget() : nullptr; - } - - // All the locks that block this new exclusive lock need to be invalidated. - // We also need to notify clients to abort operations for them. - AutoTArray lockIds; - lockIds.SetLength(Client::TypeMax()); - - const auto& blockedOnLocks = lock->GetBlockedOnLocks(); - - for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) { - if (!blockedOnLock->IsInternal()) { - blockedOnLock->Invalidate(); - - // Clients don't have to handle pending locks. Invalidation is sufficient - // in that case (once a lock is ready and the listener needs to be - // notified, we will call DirectoryLockFailed instead of - // DirectoryLockAcquired which should release any remaining references to - // the lock). - if (!blockedOnLock->IsPending()) { - lockIds[blockedOnLock->ClientType()].Put(blockedOnLock->Id()); - } - } - } - - for (Client::Type type : AllClientTypes()) { - if (lockIds[type].Filled()) { - (*mClients)[type]->AbortOperationsForLocks(lockIds[type]); - } - } - - return blocked ? lock.forget() : nullptr; + return lock.forget(); } Result, bool>, nsresult> @@ -7807,9 +7832,11 @@ void NormalOriginOperationBase::Open() { AdvanceState(); if (mNeedsDirectoryLocking) { - RefPtr pendingDirectoryLock = - QuotaManager::Get()->OpenDirectoryInternal( - mPersistenceType, mOriginScope, mClientType, mExclusive, this); + RefPtr directoryLock = + QuotaManager::Get()->CreateDirectoryLockInternal( + mPersistenceType, mOriginScope, mClientType, mExclusive); + + directoryLock->Acquire(this); } else { QM_TRY(DirectoryOpen(), QM_VOID, [this](const nsresult rv) { Finish(rv); }); } diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index 9dad7942c302..b151be619857 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -73,6 +73,8 @@ class NS_NO_VTABLE RefCountedObject { NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING }; +class OpenDirectoryListener; + class DirectoryLock : public RefCountedObject { friend class DirectoryLockImpl; @@ -88,6 +90,8 @@ class DirectoryLock : public RefCountedObject { Client::Type ClientType() const; + void Acquire(RefPtr aOpenListener); + already_AddRefed Specialize( PersistenceType aPersistenceType, const quota::GroupAndOrigin& aGroupAndOrigin, @@ -259,6 +263,10 @@ class QuotaManager final : public BackgroundThreadObject { void PersistOrigin(const GroupAndOrigin& aGroupAndOrigin); + using DirectoryLockIdTableArray = + AutoTArray; + void AbortOperationsForLocks(const DirectoryLockIdTableArray& aLockIds); + // Called when a process is being shot down. Aborts any running operations // for the given process. void AbortOperationsForProcess(ContentParentId aContentParentId); @@ -305,17 +313,15 @@ class QuotaManager final : public BackgroundThreadObject { // Unlocking is simply done by dropping all references to the lock object. // In other words, protection which the lock represents dies with the lock // object itself. - already_AddRefed OpenDirectory( + already_AddRefed CreateDirectoryLock( PersistenceType aPersistenceType, const GroupAndOrigin& aGroupAndOrigin, - Client::Type aClientType, bool aExclusive, - RefPtr aOpenListener); + Client::Type aClientType, bool aExclusive); // XXX RemoveMe once bug 1170279 gets fixed. - already_AddRefed OpenDirectoryInternal( + already_AddRefed CreateDirectoryLockInternal( const Nullable& aPersistenceType, const OriginScope& aOriginScope, - const Nullable& aClientType, bool aExclusive, - OpenDirectoryListener* aOpenListener); + const Nullable& aClientType, bool aExclusive); // Collect inactive and the least recently used origins. uint64_t CollectOriginsForEviction( @@ -484,8 +490,7 @@ class QuotaManager final : public BackgroundThreadObject { const Nullable& aPersistenceType, const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, bool aExclusive, - bool aInternal, RefPtr aOpenListener, - bool& aBlockedOut); + bool aInternal); already_AddRefed CreateDirectoryLockForEviction( PersistenceType aPersistenceType, const GroupAndOrigin& aGroupAndOrigin); @@ -494,6 +499,8 @@ class QuotaManager final : public BackgroundThreadObject { void UnregisterDirectoryLock(DirectoryLockImpl& aLock); + void AddPendingDirectoryLock(DirectoryLockImpl& aLock); + void RemovePendingDirectoryLock(DirectoryLockImpl& aLock); uint64_t LockedCollectOriginsForEviction( diff --git a/dom/simpledb/ActorsParent.cpp b/dom/simpledb/ActorsParent.cpp index ccda2f973622..16ad475624dc 100644 --- a/dom/simpledb/ActorsParent.cpp +++ b/dom/simpledb/ActorsParent.cpp @@ -1178,12 +1178,14 @@ nsresult OpenOp::OpenDirectory() { MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); MOZ_ASSERT(QuotaManager::Get()); + RefPtr directoryLock = + QuotaManager::Get()->CreateDirectoryLock( + GetConnection()->GetPersistenceType(), mQuotaInfo, + mozilla::dom::quota::Client::SDB, + /* aExclusive */ false); + mState = State::DirectoryOpenPending; - RefPtr pendingDirectoryLock = - QuotaManager::Get()->OpenDirectory(GetConnection()->GetPersistenceType(), - mQuotaInfo, - mozilla::dom::quota::Client::SDB, - /* aExclusive */ false, this); + directoryLock->Acquire(this); return NS_OK; }