diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 20140416ecca..92918ee69aa7 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -952,13 +952,13 @@ class OriginInfo final { class OriginInfoLRUComparator { public: - bool Equals(const NotNull>& a, - const NotNull>& b) const { + bool Equals(const NotNull>& a, + const NotNull>& b) const { return a->LockedAccessTime() == b->LockedAccessTime(); } - bool LessThan(const NotNull>& a, - const NotNull>& b) const { + bool LessThan(const NotNull>& a, + const NotNull>& b) const { return a->LockedAccessTime() < b->LockedAccessTime(); } }; @@ -3783,8 +3783,8 @@ uint64_t QuotaManager::CollectOriginsForEviction( static void GetInactiveOriginInfos( const nsTArray>>& aOriginInfos, const nsTArray>& aLocks, - nsTArray>>& aInactiveOriginInfos) { - for (const NotNull>& originInfo : aOriginInfos) { + OriginInfosFlatTraversable& aInactiveOriginInfos) { + for (const auto& originInfo : aOriginInfos) { MOZ_ASSERT(originInfo->mGroupInfo->mPersistenceType != PERSISTENCE_TYPE_PERSISTENT); @@ -3832,7 +3832,7 @@ uint64_t QuotaManager::CollectOriginsForEviction( } } - nsTArray>> inactiveOrigins; + OriginInfosFlatTraversable inactiveOrigins; // Enumerate and process inactive origins. This must be protected by the // mutex. @@ -6549,7 +6549,7 @@ nsresult QuotaManager::EnsureTemporaryStorageIsInitialized() { mTemporaryStorageLimit = GetTemporaryStorageLimit( /* aAvailableSpaceBytes */ diskSpaceAvailable + mTemporaryStorageUsage); - CheckTemporaryStorageLimits(); + CleanupTemporaryStorage(); return NS_OK; } @@ -7091,150 +7091,195 @@ already_AddRefed QuotaManager::LockedGetOriginInfo( return nullptr; } -void QuotaManager::CheckTemporaryStorageLimits() { - AssertIsOnIOThread(); +QuotaManager::OriginInfosFlatTraversable +QuotaManager::LockedGetOriginInfosExceedingGroupLimit() const { + mQuotaMutex.AssertCurrentThreadOwns(); - const auto doomedOrigins = [this] { - const auto doomedOriginInfos = [this] { - nsTArray>> doomedOriginInfos; - MutexAutoLock lock(mQuotaMutex); + OriginInfosFlatTraversable originInfos; - for (const auto& entry : mGroupInfoPairs) { - const auto& pair = entry.GetData(); + for (const auto& entry : mGroupInfoPairs) { + const auto& pair = entry.GetData(); - MOZ_ASSERT(!entry.GetKey().IsEmpty()); - MOZ_ASSERT(pair); + MOZ_ASSERT(!entry.GetKey().IsEmpty()); + MOZ_ASSERT(pair); - uint64_t groupUsage = 0; + uint64_t groupUsage = 0; - RefPtr temporaryGroupInfo = - pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); - if (temporaryGroupInfo) { - groupUsage += temporaryGroupInfo->mUsage; - } + const RefPtr temporaryGroupInfo = + pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (temporaryGroupInfo) { + groupUsage += temporaryGroupInfo->mUsage; + } - RefPtr defaultGroupInfo = - pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); - if (defaultGroupInfo) { - groupUsage += defaultGroupInfo->mUsage; - } + const RefPtr defaultGroupInfo = + pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); + if (defaultGroupInfo) { + groupUsage += defaultGroupInfo->mUsage; + } - if (groupUsage > 0) { - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager, "Shouldn't be null!"); + if (groupUsage > 0) { + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager, "Shouldn't be null!"); - if (groupUsage > quotaManager->GetGroupLimit()) { - nsTArray>> originInfos; - if (temporaryGroupInfo) { - originInfos.AppendElements(temporaryGroupInfo->mOriginInfos); - } - if (defaultGroupInfo) { - originInfos.AppendElements(defaultGroupInfo->mOriginInfos); - } - originInfos.Sort(OriginInfoLRUComparator()); - - for (uint32_t i = 0; i < originInfos.Length(); i++) { - const NotNull>& originInfo = originInfos[i]; - if (originInfo->LockedPersisted()) { - continue; - } - - doomedOriginInfos.AppendElement(originInfo); - groupUsage -= originInfo->LockedUsage(); - - if (groupUsage <= quotaManager->GetGroupLimit()) { - break; - } - } + if (groupUsage > quotaManager->GetGroupLimit()) { + const auto allOriginInfosLRUSorted = [&temporaryGroupInfo, + &defaultGroupInfo] { + OriginInfosFlatTraversable originInfos; + if (temporaryGroupInfo) { + originInfos.AppendElements(temporaryGroupInfo->mOriginInfos); } - } - } + if (defaultGroupInfo) { + originInfos.AppendElements(defaultGroupInfo->mOriginInfos); + } + originInfos.Sort(OriginInfoLRUComparator()); + return originInfos; + }(); - uint64_t usage = std::accumulate( - doomedOriginInfos.cbegin(), doomedOriginInfos.cend(), uint64_t(0), - [](uint64_t oldValue, const auto& originInfo) { - return oldValue + originInfo->LockedUsage(); - }); - - if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) { - nsTArray>> originInfos; - - for (const auto& entry : mGroupInfoPairs) { - const auto& pair = entry.GetData(); - - MOZ_ASSERT(!entry.GetKey().IsEmpty()); - MOZ_ASSERT(pair); - - RefPtr groupInfo = - pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); - if (groupInfo) { - originInfos.AppendElements(groupInfo->mOriginInfos); + for (const auto& originInfo : allOriginInfosLRUSorted) { + // XXX We can remove these before sorting and then just truncate the + // existing array at the right point. + if (originInfo->LockedPersisted()) { + continue; } - groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); - if (groupInfo) { - originInfos.AppendElements(groupInfo->mOriginInfos); - } - } + originInfos.AppendElement(originInfo); + groupUsage -= originInfo->LockedUsage(); - originInfos.RemoveElementsBy( - [&doomedOriginInfos](const auto& originInfo) { - return doomedOriginInfos.Contains(originInfo) || - originInfo->LockedPersisted(); - }); - - originInfos.Sort(OriginInfoLRUComparator()); - - for (uint32_t i = 0; i < originInfos.Length(); i++) { - if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) { - originInfos.TruncateLength(i); + if (groupUsage <= quotaManager->GetGroupLimit()) { break; } - - usage += originInfos[i]->LockedUsage(); } - - doomedOriginInfos.AppendElements(originInfos); } + } + } - return doomedOriginInfos; - }(); + return originInfos; +} - for (const auto& doomedOriginInfo : doomedOriginInfos) { -#ifdef DEBUG - { - MutexAutoLock lock(mQuotaMutex); - MOZ_ASSERT(!doomedOriginInfo->LockedPersisted()); - } -#endif +QuotaManager::OriginInfosFlatTraversable +QuotaManager::LockedGetOriginInfosExceedingGlobalLimit( + const OriginInfosFlatTraversable& aAlreadyDoomedOriginInfos, + const uint64_t aAlreadyDoomedUsage) const { + mQuotaMutex.AssertCurrentThreadOwns(); - DeleteFilesForOrigin(doomedOriginInfo->mGroupInfo->mPersistenceType, - doomedOriginInfo->mOrigin); + OriginInfosFlatTraversable originInfos; + for (const auto& entry : mGroupInfoPairs) { + const auto& pair = entry.GetData(); + + MOZ_ASSERT(!entry.GetKey().IsEmpty()); + MOZ_ASSERT(pair); + + RefPtr groupInfo = + pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + originInfos.AppendElements(groupInfo->mOriginInfos); } - nsTArray doomedOrigins; + groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); + if (groupInfo) { + originInfos.AppendElements(groupInfo->mOriginInfos); + } + } + + originInfos.RemoveElementsBy( + [&aAlreadyDoomedOriginInfos](const auto& originInfo) { + return aAlreadyDoomedOriginInfos.Contains(originInfo) || + originInfo->LockedPersisted(); + }); + + originInfos.Sort(OriginInfoLRUComparator()); + + uint64_t doomedUsage = 0; + for (uint32_t i = 0; i < originInfos.Length(); i++) { + if (mTemporaryStorageUsage - aAlreadyDoomedUsage - doomedUsage <= + mTemporaryStorageLimit) { + originInfos.TruncateLength(i); + break; + } + + doomedUsage += originInfos[i]->LockedUsage(); + } + + return originInfos; +} + +QuotaManager::OriginInfosFlatTraversable +QuotaManager::GetOriginInfosExceedingLimits() const { + MutexAutoLock lock(mQuotaMutex); + + auto originInfos = LockedGetOriginInfosExceedingGroupLimit(); + + const uint64_t doomedUsage = + std::accumulate(originInfos.cbegin(), originInfos.cend(), uint64_t(0), + [](uint64_t oldValue, const auto& originInfo) { + return oldValue + originInfo->LockedUsage(); + }); + + // Evicting origins that exceed their group limit also affects the global + // temporary storage usage. If the global temporary storage limit would still + // be exceeded after evicting the origins that were already selected, we need + // to specifically evict origins to get below the global limit. + if (mTemporaryStorageUsage - doomedUsage > mTemporaryStorageLimit) { + originInfos.AppendElements( + LockedGetOriginInfosExceedingGlobalLimit(originInfos, doomedUsage)); + } + + return originInfos; +} + +void QuotaManager::ClearOrigins( + const OriginInfosFlatTraversable& aDoomedOriginInfos) { + AssertIsOnIOThread(); + + // XXX Does this need to be done a) in order and/or b) sequentially? + // XXX We don't need to concatenate the results of the two steps. It would be + // sufficient to chain the ranges for iteration. + for (const auto& doomedOriginInfo : aDoomedOriginInfos) { +#ifdef DEBUG { MutexAutoLock lock(mQuotaMutex); - - for (const auto& doomedOriginInfo : doomedOriginInfos) { - PersistenceType persistenceType = - doomedOriginInfo->mGroupInfo->mPersistenceType; - const GroupAndOrigin groupAndOrigin = { - doomedOriginInfo->mGroupInfo->mGroup, doomedOriginInfo->mOrigin}; - LockedRemoveQuotaForOrigin(persistenceType, groupAndOrigin); - - doomedOrigins.EmplaceBack( - OriginParams(persistenceType, groupAndOrigin.mOrigin)); - } + MOZ_ASSERT(!doomedOriginInfo->LockedPersisted()); } +#endif - return doomedOrigins; - }(); + DeleteFilesForOrigin(doomedOriginInfo->mGroupInfo->mPersistenceType, + doomedOriginInfo->mOrigin); + } - for (const OriginParams& doomedOrigin : doomedOrigins) { - OriginClearCompleted(doomedOrigin.mPersistenceType, doomedOrigin.mOrigin, + struct OriginParams { + nsCString mOrigin; + PersistenceType mPersistenceType; + }; + + nsTArray clearedOrigins; + + { + MutexAutoLock lock(mQuotaMutex); + + for (const auto& doomedOriginInfo : aDoomedOriginInfos) { + // LockedRemoveQuotaForOrigin might remove the group info; + // OriginInfo::mGroupInfo is only a raw pointer, so we need to store the + // information for calling OriginClearCompleted below in a separate array. + clearedOrigins.AppendElement( + OriginParams{doomedOriginInfo->mOrigin, + doomedOriginInfo->mGroupInfo->mPersistenceType}); + + LockedRemoveQuotaForOrigin( + doomedOriginInfo->mGroupInfo->mPersistenceType, + {doomedOriginInfo->mGroupInfo->mGroup, doomedOriginInfo->mOrigin}); + } + } + + for (const auto& clearedOrigin : clearedOrigins) { + OriginClearCompleted(clearedOrigin.mPersistenceType, clearedOrigin.mOrigin, Nullable()); } +} + +void QuotaManager::CleanupTemporaryStorage() { + AssertIsOnIOThread(); + + ClearOrigins(GetOriginInfosExceedingLimits()); if (mTemporaryStorageUsage > mTemporaryStorageLimit) { // If disk space is still low after origin clear, notify storage pressure. diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index b41cd8b3a634..0c81cd50f064 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -114,14 +114,6 @@ class NS_NO_VTABLE OpenDirectoryListener : public RefCountedObject { virtual ~OpenDirectoryListener() = default; }; -struct OriginParams { - OriginParams(PersistenceType aPersistenceType, const nsACString& aOrigin) - : mOrigin(aOrigin), mPersistenceType(aPersistenceType) {} - - nsCString mOrigin; - PersistenceType mPersistenceType; -}; - class QuotaManager final : public BackgroundThreadObject { friend class DirectoryLockImpl; friend class GroupInfo; @@ -566,7 +558,20 @@ class QuotaManager final : public BackgroundThreadObject { int64_t aAccessTime, bool aPersisted, nsIFile* aDirectory); - void CheckTemporaryStorageLimits(); + using OriginInfosFlatTraversable = + nsTArray>>; + + OriginInfosFlatTraversable LockedGetOriginInfosExceedingGroupLimit() const; + + OriginInfosFlatTraversable LockedGetOriginInfosExceedingGlobalLimit( + const OriginInfosFlatTraversable& aAlreadyDoomedOriginInfos, + uint64_t aAlreadyDoomedUsage) const; + + OriginInfosFlatTraversable GetOriginInfosExceedingLimits() const; + + void ClearOrigins(const OriginInfosFlatTraversable& aDoomedOriginInfos); + + void CleanupTemporaryStorage(); void DeleteFilesForOrigin(PersistenceType aPersistenceType, const nsACString& aOrigin); @@ -605,7 +610,7 @@ class QuotaManager final : public BackgroundThreadObject { // Accesses to mQuotaManagerShutdownSteps must be protected by mQuotaMutex. nsCString mQuotaManagerShutdownSteps; - mozilla::Mutex mQuotaMutex; + mutable mozilla::Mutex mQuotaMutex; nsClassHashtable mGroupInfoPairs; diff --git a/mfbt/NotNull.h b/mfbt/NotNull.h index bbb4796ec30e..b434bb64ae1b 100644 --- a/mfbt/NotNull.h +++ b/mfbt/NotNull.h @@ -77,7 +77,10 @@ struct CopyablePtr { T mPtr; template - explicit CopyablePtr(U aPtr) : mPtr{std::move(aPtr)} {} + explicit CopyablePtr(U&& aPtr) : mPtr{std::forward(aPtr)} {} + + template + explicit CopyablePtr(CopyablePtr aPtr) : mPtr{std::move(aPtr.mPtr)} {} }; } // namespace detail