/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DOM_QUOTA_DIRECTORYLOCKIMPL_H_ #define DOM_QUOTA_DIRECTORYLOCKIMPL_H_ #include #include #include #include "nsISupportsImpl.h" #include "nsTArray.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/MozPromise.h" #include "mozilla/NotNull.h" #include "mozilla/RefPtr.h" #include "mozilla/dom/FlippedOnce.h" #include "mozilla/dom/Nullable.h" #include "mozilla/dom/quota/Client.h" #include "mozilla/dom/quota/ClientStorageScope.h" #include "mozilla/dom/quota/CommonMetadata.h" #include "mozilla/dom/quota/DirectoryLockCategory.h" #include "mozilla/dom/quota/ForwardDecls.h" #include "mozilla/dom/quota/OriginScope.h" #include "mozilla/dom/quota/PersistenceScope.h" #include "mozilla/dom/quota/PersistenceType.h" #include "nsCOMPtr.h" class nsITimer; namespace mozilla::dom::quota { class ClientDirectoryLockHandle; struct OriginMetadata; class QuotaManager; enum class ShouldUpdateLockIdTableFlag { No, Yes }; // XXX Rename to DirectoryLockBase. class DirectoryLockImpl { public: class PrepareInfo; private: friend class ClientDirectoryLock; friend class ClientDirectoryLockHandle; friend class OriginDirectoryLock; friend class QuotaManager; friend class UniversalDirectoryLock; const NotNull> mQuotaManager; const PersistenceScope mPersistenceScope; const OriginScope mOriginScope; const ClientStorageScope mClientStorageScope; MozPromiseHolder mAcquirePromiseHolder; nsCOMPtr mAcquireTimer; nsTArray> mBlocking; nsTArray> mBlockedOn; std::function mInvalidateCallback; const int64_t mId; const bool mExclusive; // Internal quota manager operations use this flag to prevent directory lock // registraction/unregistration from updating origin access time, etc. const bool mInternal; const bool mShouldUpdateLockIdTable; const DirectoryLockCategory mCategory; bool mRegistered; FlippedOnce mPending; FlippedOnce mAcquired; FlippedOnce mInvalidated; FlippedOnce mDropped; public: DirectoryLockImpl(MovingNotNull> aQuotaManager, const PersistenceScope& aPersistenceScope, const OriginScope& aOriginScope, const ClientStorageScope& aClientStorageScope, bool aExclusive, bool aInternal, ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag, DirectoryLockCategory aCategory); NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl) int64_t Id() const { return mId; } const PersistenceScope& PersistenceScopeRef() const { return mPersistenceScope; } const OriginScope& GetOriginScope() const { return mOriginScope; } const ClientStorageScope& ClientStorageScopeRef() const { return mClientStorageScope; } DirectoryLockCategory Category() const { return mCategory; } bool Acquired() const { return mAcquired; } bool MustWait() const; nsTArray> LocksMustWaitFor() const; bool Invalidated() const { return mInvalidated; } bool Dropped() const { return mDropped; } PrepareInfo Prepare() const; RefPtr Acquire(); RefPtr Acquire(PrepareInfo&& aPrepareInfo); void AcquireImmediately(); void AssertIsAcquiredExclusively() #ifdef DEBUG ; #else { } #endif RefPtr Drop(); void OnInvalidate(std::function&& aCallback); void Log() const; private: virtual ~DirectoryLockImpl(); void AssertIsOnOwningThread() const #ifdef DEBUG ; #else { } #endif PersistenceType GetPersistenceType() const { MOZ_DIAGNOSTIC_ASSERT(mPersistenceScope.IsValue()); return mPersistenceScope.GetValue(); } quota::OriginMetadata OriginMetadata() const { MOZ_DIAGNOSTIC_ASSERT(mOriginScope.IsOrigin()); return quota::OriginMetadata{mOriginScope.GetPrincipalMetadata(), GetPersistenceType()}; } const nsACString& Origin() const { MOZ_DIAGNOSTIC_ASSERT(mOriginScope.IsOrigin()); MOZ_DIAGNOSTIC_ASSERT(!mOriginScope.GetOrigin().IsEmpty()); return mOriginScope.GetOrigin(); } Client::Type ClientType() const { MOZ_DIAGNOSTIC_ASSERT(mClientStorageScope.IsClient()); MOZ_DIAGNOSTIC_ASSERT(mClientStorageScope.GetClientType() < Client::TypeMax()); return mClientStorageScope.GetClientType(); } bool IsInternal() const { return mInternal; } void SetRegistered(bool aRegistered) { mRegistered = aRegistered; } bool IsPending() const { return mPending; } // Ideally, we would have just one table (instead of these two: // QuotaManager::mDirectoryLocks and QuotaManager::mDirectoryLockIdTable) for // all registered locks. However, some directory locks need to be accessed off // the PBackground thread, so the access must be protected by the quota mutex. // The problem is that directory locks for eviction must be currently created // while the mutex lock is already acquired. So we decided to have two tables // for now and to not register directory locks for eviction in // QuotaManager::mDirectoryLockIdTable. This can be improved in future after // some refactoring of the mutex locking. bool ShouldUpdateLockIdTable() const { return mShouldUpdateLockIdTable; } bool ShouldUpdateLockTable() { return !mInternal && mPersistenceScope.GetValue() != PERSISTENCE_TYPE_PERSISTENT; } bool Overlaps(const DirectoryLockImpl& aLock) const; // Test whether this DirectoryLock needs to wait for the given lock. bool MustWaitFor(const DirectoryLockImpl& aLock) const; void AddBlockingLock(DirectoryLockImpl& aLock) { AssertIsOnOwningThread(); mBlocking.AppendElement(WrapNotNull(&aLock)); } const nsTArray>& GetBlockedOnLocks() { return mBlockedOn; } void AddBlockedOnLock(DirectoryLockImpl& aLock) { AssertIsOnOwningThread(); mBlockedOn.AppendElement(WrapNotNull(&aLock)); } void MaybeUnblock(DirectoryLockImpl& aLock) { AssertIsOnOwningThread(); mBlockedOn.RemoveElement(&aLock); if (mBlockedOn.IsEmpty()) { NotifyOpenListener(); } } void NotifyOpenListener(); template nsTArray LocksMustWaitForInternal() const; void AcquireInternal(PrepareInfo&& aPrepareInfo); void Invalidate(); void Unregister(); }; class MOZ_RAII DirectoryLockImpl::PrepareInfo { friend class DirectoryLockImpl; nsTArray> mBlockedOn; public: // Disable copy constructor and assignment operator PrepareInfo(const PrepareInfo&) = delete; PrepareInfo& operator=(const PrepareInfo&) = delete; // Move constructor and move assignment operator PrepareInfo(PrepareInfo&&) noexcept = default; PrepareInfo& operator=(PrepareInfo&&) noexcept = default; const nsTArray>& BlockedOnRef() const { return mBlockedOn; } private: explicit PrepareInfo(const DirectoryLockImpl& aDirectoryLock) : mBlockedOn( aDirectoryLock .LocksMustWaitForInternal>()) {} }; } // namespace mozilla::dom::quota #endif // DOM_QUOTA_DIRECTORYLOCKIMPL_H_