DirectoryLockImpl.h (8350B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef DOM_QUOTA_DIRECTORYLOCKIMPL_H_ 8 #define DOM_QUOTA_DIRECTORYLOCKIMPL_H_ 9 10 #include <cstdint> 11 #include <functional> 12 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Attributes.h" 15 #include "mozilla/EnumSet.h" 16 #include "mozilla/MozPromise.h" 17 #include "mozilla/NotNull.h" 18 #include "mozilla/RefPtr.h" 19 #include "mozilla/dom/FlippedOnce.h" 20 #include "mozilla/dom/Nullable.h" 21 #include "mozilla/dom/quota/Client.h" 22 #include "mozilla/dom/quota/ClientStorageScope.h" 23 #include "mozilla/dom/quota/CommonMetadata.h" 24 #include "mozilla/dom/quota/DirectoryLockCategory.h" 25 #include "mozilla/dom/quota/ForwardDecls.h" 26 #include "mozilla/dom/quota/OriginScope.h" 27 #include "mozilla/dom/quota/PersistenceScope.h" 28 #include "mozilla/dom/quota/PersistenceType.h" 29 #include "nsCOMPtr.h" 30 #include "nsISupportsImpl.h" 31 #include "nsTArray.h" 32 33 class nsITimer; 34 35 namespace mozilla::dom::quota { 36 37 class ClientDirectoryLockHandle; 38 struct OriginMetadata; 39 class QuotaManager; 40 41 enum class ShouldUpdateLockIdTableFlag { No, Yes }; 42 43 // XXX Rename to DirectoryLockBase. 44 class DirectoryLockImpl { 45 public: 46 class PrepareInfo; 47 48 private: 49 friend class ClientDirectoryLock; 50 friend class ClientDirectoryLockHandle; 51 friend class OriginDirectoryLock; 52 friend class QuotaManager; 53 friend class UniversalDirectoryLock; 54 55 const NotNull<RefPtr<QuotaManager>> mQuotaManager; 56 57 const PersistenceScope mPersistenceScope; 58 const OriginScope mOriginScope; 59 const ClientStorageScope mClientStorageScope; 60 61 MozPromiseHolder<BoolPromise> mAcquirePromiseHolder; 62 nsCOMPtr<nsITimer> mAcquireTimer; 63 64 nsTArray<NotNull<DirectoryLockImpl*>> mBlocking; 65 nsTArray<NotNull<DirectoryLockImpl*>> mBlockedOn; 66 67 std::function<void()> mInvalidateCallback; 68 69 const int64_t mId; 70 71 const bool mExclusive; 72 73 // Internal quota manager operations use this flag to prevent directory lock 74 // registraction/unregistration from updating origin access time, etc. 75 const bool mInternal; 76 77 const bool mShouldUpdateLockIdTable; 78 79 const DirectoryLockCategory mCategory; 80 81 bool mRegistered; 82 FlippedOnce<true> mPending; 83 FlippedOnce<false> mAcquired; 84 FlippedOnce<false> mInvalidated; 85 FlippedOnce<false> mDropped; 86 87 public: 88 DirectoryLockImpl(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, 89 const PersistenceScope& aPersistenceScope, 90 const OriginScope& aOriginScope, 91 const ClientStorageScope& aClientStorageScope, 92 bool aExclusive, bool aInternal, 93 ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag, 94 DirectoryLockCategory aCategory); 95 96 NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl) 97 98 QuotaManager& MutableManagerRef() const { return *mQuotaManager; } 99 100 int64_t Id() const { return mId; } 101 102 const PersistenceScope& PersistenceScopeRef() const { 103 return mPersistenceScope; 104 } 105 106 const OriginScope& GetOriginScope() const { return mOriginScope; } 107 108 const ClientStorageScope& ClientStorageScopeRef() const { 109 return mClientStorageScope; 110 } 111 112 DirectoryLockCategory Category() const { return mCategory; } 113 114 bool Acquired() const { return mAcquired; } 115 116 bool MustWait() const; 117 118 nsTArray<RefPtr<DirectoryLockImpl>> LocksMustWaitFor() const; 119 120 bool Invalidated() const { return mInvalidated; } 121 122 bool Dropped() const { return mDropped; } 123 124 PrepareInfo Prepare() const; 125 126 RefPtr<BoolPromise> Acquire(); 127 128 RefPtr<BoolPromise> Acquire(PrepareInfo&& aPrepareInfo); 129 130 void AcquireImmediately(); 131 132 void AssertIsAcquiredExclusively() 133 #ifdef DEBUG 134 ; 135 #else 136 { 137 } 138 #endif 139 140 RefPtr<BoolPromise> Drop(); 141 142 void OnInvalidate(std::function<void()>&& aCallback); 143 144 void Log() const; 145 146 private: 147 virtual ~DirectoryLockImpl(); 148 149 void AssertIsOnOwningThread() const 150 #ifdef DEBUG 151 ; 152 #else 153 { 154 } 155 #endif 156 157 PersistenceType GetPersistenceType() const { 158 MOZ_DIAGNOSTIC_ASSERT(mPersistenceScope.IsValue()); 159 160 return mPersistenceScope.GetValue(); 161 } 162 163 quota::OriginMetadata OriginMetadata() const { 164 MOZ_DIAGNOSTIC_ASSERT(mOriginScope.IsOrigin()); 165 166 return quota::OriginMetadata{mOriginScope.GetPrincipalMetadata(), 167 GetPersistenceType()}; 168 } 169 170 const nsACString& Origin() const { 171 MOZ_DIAGNOSTIC_ASSERT(mOriginScope.IsOrigin()); 172 MOZ_DIAGNOSTIC_ASSERT(!mOriginScope.GetOrigin().IsEmpty()); 173 174 return mOriginScope.GetOrigin(); 175 } 176 177 Client::Type ClientType() const { 178 MOZ_DIAGNOSTIC_ASSERT(mClientStorageScope.IsClient()); 179 MOZ_DIAGNOSTIC_ASSERT(mClientStorageScope.GetClientType() < 180 Client::TypeMax()); 181 182 return mClientStorageScope.GetClientType(); 183 } 184 185 bool IsInternal() const { return mInternal; } 186 187 void SetRegistered(bool aRegistered) { mRegistered = aRegistered; } 188 189 bool IsPending() const { return mPending; } 190 191 // Ideally, we would have just one table (instead of these two: 192 // QuotaManager::mDirectoryLocks and QuotaManager::mDirectoryLockIdTable) for 193 // all registered locks. However, some directory locks need to be accessed off 194 // the PBackground thread, so the access must be protected by the quota mutex. 195 // The problem is that directory locks for eviction must be currently created 196 // while the mutex lock is already acquired. So we decided to have two tables 197 // for now and to not register directory locks for eviction in 198 // QuotaManager::mDirectoryLockIdTable. This can be improved in future after 199 // some refactoring of the mutex locking. 200 bool ShouldUpdateLockIdTable() const { return mShouldUpdateLockIdTable; } 201 202 bool Overlaps(const DirectoryLockImpl& aLock) const; 203 204 // Test whether this DirectoryLock needs to wait for the given lock. 205 bool MustWaitFor(const DirectoryLockImpl& aLock) const; 206 207 void AddBlockingLock(DirectoryLockImpl& aLock) { 208 AssertIsOnOwningThread(); 209 210 mBlocking.AppendElement(WrapNotNull(&aLock)); 211 } 212 213 const nsTArray<NotNull<DirectoryLockImpl*>>& GetBlockedOnLocks() { 214 return mBlockedOn; 215 } 216 217 void AddBlockedOnLock(DirectoryLockImpl& aLock) { 218 AssertIsOnOwningThread(); 219 220 mBlockedOn.AppendElement(WrapNotNull(&aLock)); 221 } 222 223 void MaybeUnblock(DirectoryLockImpl& aLock) { 224 AssertIsOnOwningThread(); 225 226 mBlockedOn.RemoveElement(&aLock); 227 if (mBlockedOn.IsEmpty()) { 228 NotifyOpenListener(); 229 } 230 } 231 232 void NotifyOpenListener(); 233 234 template <typename T> 235 nsTArray<T> LocksMustWaitForInternal() const; 236 237 void AcquireInternal(PrepareInfo&& aPrepareInfo); 238 239 void Invalidate(); 240 241 void Unregister(); 242 }; 243 244 class MOZ_RAII DirectoryLockImpl::PrepareInfo { 245 friend class DirectoryLockImpl; 246 247 nsTArray<NotNull<DirectoryLockImpl*>> mBlockedOn; 248 249 public: 250 // Disable copy constructor and assignment operator 251 PrepareInfo(const PrepareInfo&) = delete; 252 PrepareInfo& operator=(const PrepareInfo&) = delete; 253 254 // Move constructor and move assignment operator 255 PrepareInfo(PrepareInfo&&) noexcept = default; 256 PrepareInfo& operator=(PrepareInfo&&) noexcept = default; 257 258 const nsTArray<NotNull<DirectoryLockImpl*>>& BlockedOnRef() const { 259 return mBlockedOn; 260 } 261 262 /** 263 * Returns true if this directory lock would be blocked by any other lock 264 * whose category is included in the given set. 265 * 266 * Used to detect whether an initialization operation should still run, even 267 * if the cached state indicates it has already been performed, because an 268 * in-progress or pending uninitialization operation will eventually 269 * invalidate that state. 270 */ 271 bool IsBlockedBy(const EnumSet<DirectoryLockCategory>& aCategories) const { 272 return std::any_of(mBlockedOn.cbegin(), mBlockedOn.cend(), 273 [&aCategories](const auto& lock) { 274 return aCategories.contains(lock->Category()); 275 }); 276 } 277 278 private: 279 explicit PrepareInfo(const DirectoryLockImpl& aDirectoryLock) 280 : mBlockedOn( 281 aDirectoryLock 282 .LocksMustWaitForInternal<NotNull<DirectoryLockImpl*>>()) {} 283 }; 284 285 } // namespace mozilla::dom::quota 286 287 #endif // DOM_QUOTA_DIRECTORYLOCKIMPL_H_