Bug 1685677 - Don't unnecessarily materialize a flattened array of OriginInfo*. r=dom-workers-and-storage-reviewers,janv

Differential Revision: https://phabricator.services.mozilla.com/D101151
This commit is contained in:
Simon Giesecke 2021-02-02 11:33:52 +00:00
Родитель bfdca4825f
Коммит 1f967af681
5 изменённых файлов: 228 добавлений и 23 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include "ActorsParent.h" #include "ActorsParent.h"
// Local includes // Local includes
#include "Flatten.h"
#include "InitializationTypes.h" #include "InitializationTypes.h"
#include "OriginScope.h" #include "OriginScope.h"
#include "QuotaCommon.h" #include "QuotaCommon.h"
@ -7128,11 +7129,11 @@ QuotaManager::CollectLRUOriginInfosUntil(Collect&& aCollect, Pred&& aPred) {
return originInfos; return originInfos;
} }
QuotaManager::OriginInfosFlatTraversable QuotaManager::OriginInfosNestedTraversable
QuotaManager::LockedGetOriginInfosExceedingGroupLimit() const { QuotaManager::LockedGetOriginInfosExceedingGroupLimit() const {
mQuotaMutex.AssertCurrentThreadOwns(); mQuotaMutex.AssertCurrentThreadOwns();
OriginInfosFlatTraversable originInfos; OriginInfosNestedTraversable originInfos;
for (const auto& entry : mGroupInfoPairs) { for (const auto& entry : mGroupInfoPairs) {
const auto& pair = entry.GetData(); const auto& pair = entry.GetData();
@ -7159,9 +7160,7 @@ QuotaManager::LockedGetOriginInfosExceedingGroupLimit() const {
MOZ_ASSERT(quotaManager, "Shouldn't be null!"); MOZ_ASSERT(quotaManager, "Shouldn't be null!");
if (groupUsage > quotaManager->GetGroupLimit()) { if (groupUsage > quotaManager->GetGroupLimit()) {
// XXX Instead of appending into a flat array, return an array of originInfos.AppendElement(CollectLRUOriginInfosUntil(
// arrays.
originInfos.AppendElements(CollectLRUOriginInfosUntil(
[&temporaryGroupInfo, &defaultGroupInfo](auto inserter) { [&temporaryGroupInfo, &defaultGroupInfo](auto inserter) {
MaybeInsertOriginInfos(std::move(inserter), temporaryGroupInfo, MaybeInsertOriginInfos(std::move(inserter), temporaryGroupInfo,
defaultGroupInfo, defaultGroupInfo,
@ -7183,7 +7182,7 @@ QuotaManager::LockedGetOriginInfosExceedingGroupLimit() const {
QuotaManager::OriginInfosFlatTraversable QuotaManager::OriginInfosFlatTraversable
QuotaManager::LockedGetOriginInfosExceedingGlobalLimit( QuotaManager::LockedGetOriginInfosExceedingGlobalLimit(
const OriginInfosFlatTraversable& aAlreadyDoomedOriginInfos, const OriginInfosNestedTraversable& aAlreadyDoomedOriginInfos,
const uint64_t aAlreadyDoomedUsage) const { const uint64_t aAlreadyDoomedUsage) const {
mQuotaMutex.AssertCurrentThreadOwns(); mQuotaMutex.AssertCurrentThreadOwns();
@ -7199,7 +7198,13 @@ QuotaManager::LockedGetOriginInfosExceedingGlobalLimit(
inserter, pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY), inserter, pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY),
pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT), pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT),
[&aAlreadyDoomedOriginInfos](const auto& originInfo) { [&aAlreadyDoomedOriginInfos](const auto& originInfo) {
return !aAlreadyDoomedOriginInfos.Contains(originInfo) && return !std::any_of(aAlreadyDoomedOriginInfos.cbegin(),
aAlreadyDoomedOriginInfos.cend(),
// XXX This should capture originInfo by
// value, but it can't due to Bug 1421435.
[&originInfo](const auto& array) {
return array.Contains(originInfo);
}) &&
!originInfo->LockedPersisted(); !originInfo->LockedPersisted();
}); });
} }
@ -7216,24 +7221,28 @@ QuotaManager::LockedGetOriginInfosExceedingGlobalLimit(
}); });
} }
QuotaManager::OriginInfosFlatTraversable QuotaManager::OriginInfosNestedTraversable
QuotaManager::GetOriginInfosExceedingLimits() const { QuotaManager::GetOriginInfosExceedingLimits() const {
MutexAutoLock lock(mQuotaMutex); MutexAutoLock lock(mQuotaMutex);
auto originInfos = LockedGetOriginInfosExceedingGroupLimit(); auto originInfos = LockedGetOriginInfosExceedingGroupLimit();
const uint64_t doomedUsage = const uint64_t doomedUsage = std::accumulate(
std::accumulate(originInfos.cbegin(), originInfos.cend(), uint64_t(0), originInfos.cbegin(), originInfos.cend(), uint64_t(0),
[](uint64_t oldValue, const auto& originInfo) { [](uint64_t oldValue, const auto& currentOriginInfos) {
return oldValue + originInfo->LockedUsage(); return std::accumulate(currentOriginInfos.cbegin(),
}); currentOriginInfos.cend(), oldValue,
[](uint64_t oldValue, const auto& originInfo) {
return oldValue + originInfo->LockedUsage();
});
});
// Evicting origins that exceed their group limit also affects the global // Evicting origins that exceed their group limit also affects the global
// temporary storage usage. If the global temporary storage limit would still // temporary storage usage. If the global temporary storage limit would still
// be exceeded after evicting the origins that were already selected, we need // be exceeded after evicting the origins that were already selected, we need
// to specifically evict origins to get below the global limit. // to specifically evict origins to get below the global limit.
if (mTemporaryStorageUsage - doomedUsage > mTemporaryStorageLimit) { if (mTemporaryStorageUsage - doomedUsage > mTemporaryStorageLimit) {
originInfos.AppendElements( originInfos.AppendElement(
LockedGetOriginInfosExceedingGlobalLimit(originInfos, doomedUsage)); LockedGetOriginInfosExceedingGlobalLimit(originInfos, doomedUsage));
} }
@ -7241,13 +7250,12 @@ QuotaManager::GetOriginInfosExceedingLimits() const {
} }
void QuotaManager::ClearOrigins( void QuotaManager::ClearOrigins(
const OriginInfosFlatTraversable& aDoomedOriginInfos) { const OriginInfosNestedTraversable& aDoomedOriginInfos) {
AssertIsOnIOThread(); AssertIsOnIOThread();
// XXX Does this need to be done a) in order and/or b) sequentially? // 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 for (const auto& doomedOriginInfo :
// sufficient to chain the ranges for iteration. Flatten<OriginInfosFlatTraversable::elem_type>(aDoomedOriginInfos)) {
for (const auto& doomedOriginInfo : aDoomedOriginInfos) {
#ifdef DEBUG #ifdef DEBUG
{ {
MutexAutoLock lock(mQuotaMutex); MutexAutoLock lock(mQuotaMutex);
@ -7269,7 +7277,8 @@ void QuotaManager::ClearOrigins(
{ {
MutexAutoLock lock(mQuotaMutex); MutexAutoLock lock(mQuotaMutex);
for (const auto& doomedOriginInfo : aDoomedOriginInfos) { for (const auto& doomedOriginInfo :
Flatten<OriginInfosFlatTraversable::elem_type>(aDoomedOriginInfos)) {
// LockedRemoveQuotaForOrigin might remove the group info; // LockedRemoveQuotaForOrigin might remove the group info;
// OriginInfo::mGroupInfo is only a raw pointer, so we need to store the // OriginInfo::mGroupInfo is only a raw pointer, so we need to store the
// information for calling OriginClearCompleted below in a separate array. // information for calling OriginClearCompleted below in a separate array.

118
dom/quota/Flatten.h Normal file
Просмотреть файл

@ -0,0 +1,118 @@
/* -*- 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_FLATTEN_H_
#define DOM_QUOTA_FLATTEN_H_
#include <iterator>
#include <type_traits>
#include <utility>
// XXX This should be moved to MFBT.
namespace mozilla::dom::quota {
namespace detail {
using std::begin;
using std::end;
template <typename T, typename NestedRange>
auto Flatten(NestedRange&& aRange) -> std::enable_if_t<
std::is_same_v<T, std::decay_t<typename decltype(begin(
std::declval<const NestedRange&>()))::value_type>>,
std::conditional_t<std::is_rvalue_reference_v<NestedRange>,
std::decay_t<NestedRange>, NestedRange>> {
return std::forward<NestedRange>(aRange);
}
template <typename T, typename NestedRange>
struct FlatIter {
using OuterIterator =
decltype(begin(std::declval<const std::decay_t<NestedRange>&>()));
using InnerIterator =
decltype(begin(*begin(std::declval<const std::decay_t<NestedRange>&>())));
explicit FlatIter(const NestedRange& aRange, OuterIterator aIter)
: mOuterIter{std::move(aIter)}, mOuterEnd{end(aRange)} {
InitInner();
}
const T& operator*() const { return *mInnerIter; }
FlatIter& operator++() {
++mInnerIter;
if (mInnerIter == mInnerEnd) {
++mOuterIter;
InitInner();
}
return *this;
}
bool operator!=(const FlatIter& aOther) const {
return mOuterIter != aOther.mOuterIter ||
(mOuterIter != mOuterEnd && mInnerIter != aOther.mInnerIter);
}
private:
void InitInner() {
while (mOuterIter != mOuterEnd) {
const typename OuterIterator::value_type& innerRange = *mOuterIter;
mInnerIter = begin(innerRange);
mInnerEnd = end(innerRange);
if (mInnerIter != mInnerEnd) {
break;
}
++mOuterIter;
}
}
OuterIterator mOuterIter;
const OuterIterator mOuterEnd;
InnerIterator mInnerIter;
InnerIterator mInnerEnd;
};
template <typename T, typename NestedRange>
struct FlatRange {
explicit FlatRange(NestedRange aRange) : mRange{std::move(aRange)} {}
auto begin() const {
using std::begin;
return FlatIter<T, NestedRange>{mRange, begin(mRange)};
}
auto end() const {
using std::end;
return FlatIter<T, NestedRange>{mRange, end(mRange)};
}
private:
NestedRange mRange;
};
template <typename T, typename NestedRange>
auto Flatten(NestedRange&& aRange) -> std::enable_if_t<
!std::is_same_v<
T, std::decay_t<typename decltype(begin(
std::declval<const std::decay_t<NestedRange>&>()))::value_type>>,
FlatRange<T, NestedRange>> {
return FlatRange<T, NestedRange>{std::forward<NestedRange>(aRange)};
}
} // namespace detail
template <typename T, typename NestedRange>
auto Flatten(NestedRange&& aRange) -> decltype(auto) {
return detail::Flatten<T>(std::forward<NestedRange>(aRange));
}
} // namespace mozilla::dom::quota
#endif

Просмотреть файл

@ -561,15 +561,18 @@ class QuotaManager final : public BackgroundThreadObject {
using OriginInfosFlatTraversable = using OriginInfosFlatTraversable =
nsTArray<NotNull<RefPtr<const OriginInfo>>>; nsTArray<NotNull<RefPtr<const OriginInfo>>>;
OriginInfosFlatTraversable LockedGetOriginInfosExceedingGroupLimit() const; using OriginInfosNestedTraversable =
nsTArray<nsTArray<NotNull<RefPtr<const OriginInfo>>>>;
OriginInfosNestedTraversable LockedGetOriginInfosExceedingGroupLimit() const;
OriginInfosFlatTraversable LockedGetOriginInfosExceedingGlobalLimit( OriginInfosFlatTraversable LockedGetOriginInfosExceedingGlobalLimit(
const OriginInfosFlatTraversable& aAlreadyDoomedOriginInfos, const OriginInfosNestedTraversable& aAlreadyDoomedOriginInfos,
uint64_t aAlreadyDoomedUsage) const; uint64_t aAlreadyDoomedUsage) const;
OriginInfosFlatTraversable GetOriginInfosExceedingLimits() const; OriginInfosNestedTraversable GetOriginInfosExceedingLimits() const;
void ClearOrigins(const OriginInfosFlatTraversable& aDoomedOriginInfos); void ClearOrigins(const OriginInfosNestedTraversable& aDoomedOriginInfos);
void CleanupTemporaryStorage(); void CleanupTemporaryStorage();

Просмотреть файл

@ -0,0 +1,74 @@
/* -*- 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/. */
#include "Flatten.h"
#include "gtest/gtest.h"
#include "mozilla/Unused.h"
#include "nsTArray.h"
namespace mozilla::dom::quota {
TEST(Flatten, FlatEmpty)
{
for (const auto& item : Flatten<int>(nsTArray<int>{})) {
Unused << item;
FAIL();
}
}
TEST(Flatten, NestedOuterEmpty)
{
for (const auto& item : Flatten<int>(nsTArray<CopyableTArray<int>>{})) {
Unused << item;
FAIL();
}
}
TEST(Flatten, NestedInnerEmpty)
{
for (const auto& item :
Flatten<int>(nsTArray<CopyableTArray<int>>{CopyableTArray<int>{}})) {
Unused << item;
FAIL();
}
}
TEST(Flatten, NestedInnerSingular)
{
nsTArray<int> flattened;
for (const auto& item :
Flatten<int>(nsTArray<CopyableTArray<int>>{CopyableTArray<int>{1}})) {
flattened.AppendElement(item);
}
EXPECT_EQ(nsTArray{1}, flattened);
}
TEST(Flatten, NestedInnerSingulars)
{
nsTArray<int> flattened;
for (const auto& item : Flatten<int>(nsTArray<CopyableTArray<int>>{
CopyableTArray<int>{1}, CopyableTArray<int>{2}})) {
flattened.AppendElement(item);
}
EXPECT_EQ((nsTArray{1, 2}), flattened);
}
TEST(Flatten, NestedInnerNonSingulars)
{
nsTArray<int> flattened;
for (const auto& item : Flatten<int>(nsTArray<CopyableTArray<int>>{
CopyableTArray<int>{1, 2}, CopyableTArray<int>{3, 4}})) {
flattened.AppendElement(item);
}
EXPECT_EQ((nsTArray{1, 2, 3, 4}), flattened);
}
} // namespace mozilla::dom::quota

Просмотреть файл

@ -7,6 +7,7 @@
UNIFIED_SOURCES = [ UNIFIED_SOURCES = [
"TestCheckedUnsafePtr.cpp", "TestCheckedUnsafePtr.cpp",
"TestEncryptedStream.cpp", "TestEncryptedStream.cpp",
"TestFlatten.cpp",
"TestQuotaCommon.cpp", "TestQuotaCommon.cpp",
"TestQuotaManager.cpp", "TestQuotaManager.cpp",
"TestUsageInfo.cpp", "TestUsageInfo.cpp",