Bug 1749504 - Add QuotaManager::InitializeStorage method; r=dom-storage-reviewers,jstutte

There's currently EnsureStorageIsInitializedInternal which can only be called
on the QuotaManager IO thread. That method shouldn't be exposed to quota
clients and origin operations. There should be a public method callable from
the PBackground thread returning a MozPromise instead. Such method will
guarantee that proper directory locking is acquired before storage
initialization is started.

Changes done in this patch:
- added QuotaManager::InitializeStorage method
- added QuotaManager::IsStorageInitialized method
- QuotaManagerService::Init reworked to use an asynchronous message instead of
  creating a sub actor
- added QuotaManagerDependencyFixture::IsStorageInitialized
- added QuotaManagerDependencyFixture::AssertStorageIsInitialized
- added QuotaManagerDependencyFixture::AssertStorageIsNotInitialized
- eliminated QuotaManager::IsStorageInitializedInternal calls in tests
- eliminated QuotaManager::EnsureStorageIsInitializedInternal calls in tests
- added a bunch of tests for the new QuotaManager::InitializeStorage method

Differential Revision: https://phabricator.services.mozilla.com/D185570
This commit is contained in:
Jan Varga 2023-09-13 16:41:56 +00:00
Родитель ac70b31bd2
Коммит 1ae113cb02
13 изменённых файлов: 795 добавлений и 80 удалений

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

@ -361,7 +361,6 @@ mozilla::ipc::IPCResult QuotaRequestChild::Recv__delete__(
aResponse.get_TemporaryStorageInitializedResponse().initialized());
break;
case RequestResponse::TInitResponse:
case RequestResponse::TInitTemporaryStorageResponse:
case RequestResponse::TClearOriginResponse:
case RequestResponse::TResetOriginResponse:

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

@ -1656,6 +1656,8 @@ QuotaManager::QuotaManager(const nsAString& aBasePath,
mStorageName(aStorageName),
mTemporaryStorageUsage(0),
mNextDirectoryLockId(0),
mShutdownStorageOpCount(0),
mStorageInitialized(false),
mTemporaryStorageInitialized(false),
mCacheUsable(false) {
AssertIsOnOwningThread();
@ -4847,6 +4849,36 @@ Result<Ok, nsresult> QuotaManager::CreateEmptyLocalStorageArchive(
return Ok{};
}
RefPtr<BoolPromise> QuotaManager::InitializeStorage() {
AssertIsOnOwningThread();
// If storage is initialized but there's a clear storage or shutdown storage
// operation already scheduled, we can't immediately resolve the promise and
// return from the function because the clear or shutdown storage operation
// uninitializes storage.
if (mStorageInitialized && !mShutdownStorageOpCount) {
return BoolPromise::CreateAndResolve(true, __func__);
}
auto initializeStorageOp = CreateInitOp();
RegisterNormalOriginOp(*initializeStorageOp);
initializeStorageOp->RunImmediately();
return initializeStorageOp->OnResults()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue& aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
self->mStorageInitialized = true;
return BoolPromise::CreateAndResolve(true, __func__);
});
}
nsresult QuotaManager::EnsureStorageIsInitializedInternal() {
DiagnosticAssertIsOnIOThread();
@ -5122,7 +5154,23 @@ RefPtr<BoolPromise> QuotaManager::ClearStorage() {
clearStorageOp->RunImmediately();
return clearStorageOp->OnResults();
// Storage clearing also shuts it down, so we need to increses the counter
// here as well.
mShutdownStorageOpCount++;
return clearStorageOp->OnResults()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue& aValue) {
self->mShutdownStorageOpCount--;
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
self->mStorageInitialized = false;
return BoolPromise::CreateAndResolve(true, __func__);
});
}
RefPtr<BoolPromise> QuotaManager::ShutdownStorage() {
@ -5134,7 +5182,21 @@ RefPtr<BoolPromise> QuotaManager::ShutdownStorage() {
shutdownStorageOp->RunImmediately();
return shutdownStorageOp->OnResults();
mShutdownStorageOpCount++;
return shutdownStorageOp->OnResults()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue& aValue) {
self->mShutdownStorageOpCount--;
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
self->mStorageInitialized = false;
return BoolPromise::CreateAndResolve(true, __func__);
});
}
void QuotaManager::ShutdownStorageInternal() {

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

@ -289,7 +289,7 @@ class TemporaryStorageInitializedOp final : public InitializedRequestBase {
void GetResponse(RequestResponse& aResponse) override;
};
class InitOp final : public QuotaRequestBase {
class InitOp final : public ResolvableNormalOriginOp<bool> {
public:
InitOp();
@ -298,7 +298,7 @@ class InitOp final : public QuotaRequestBase {
nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override;
void GetResponse(RequestResponse& aResponse) override;
bool GetResolveValue() override;
};
class InitTemporaryStorageOp final : public QuotaRequestBase {
@ -571,7 +571,9 @@ RefPtr<QuotaRequestBase> CreateTemporaryStorageInitializedOp() {
return MakeRefPtr<TemporaryStorageInitializedOp>();
}
RefPtr<QuotaRequestBase> CreateInitOp() { return MakeRefPtr<InitOp>(); }
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitOp() {
return MakeRefPtr<InitOp>();
}
RefPtr<QuotaRequestBase> CreateInitTemporaryStorageOp() {
return MakeRefPtr<InitTemporaryStorageOp>();
@ -1102,7 +1104,10 @@ void TemporaryStorageInitializedOp::GetResponse(RequestResponse& aResponse) {
}
InitOp::InitOp()
: QuotaRequestBase("dom::quota::InitOp", /* aExclusive */ false) {
: ResolvableNormalOriginOp(
"dom::quota::InitOp", Nullable<PersistenceType>(),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false) {
AssertIsOnOwningThread();
}
@ -1116,11 +1121,7 @@ nsresult InitOp::DoDirectoryWork(QuotaManager& aQuotaManager) {
return NS_OK;
}
void InitOp::GetResponse(RequestResponse& aResponse) {
AssertIsOnOwningThread();
aResponse = InitResponse();
}
bool InitOp::GetResolveValue() { return true; }
InitTemporaryStorageOp::InitTemporaryStorageOp()
: QuotaRequestBase("dom::quota::InitTemporaryStorageOp",

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

@ -53,7 +53,7 @@ RefPtr<QuotaRequestBase> CreateStorageInitializedOp();
RefPtr<QuotaRequestBase> CreateTemporaryStorageInitializedOp();
RefPtr<QuotaRequestBase> CreateInitOp();
RefPtr<ResolvableNormalOriginOp<bool>> CreateInitOp();
RefPtr<QuotaRequestBase> CreateInitTemporaryStorageOp();

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

@ -49,10 +49,6 @@ struct TemporaryStorageInitializedParams
{
};
struct InitParams
{
};
struct InitTemporaryStorageParams
{
};
@ -140,7 +136,6 @@ union RequestParams
StorageNameParams;
StorageInitializedParams;
TemporaryStorageInitializedParams;
InitParams;
InitTemporaryStorageParams;
InitializePersistentOriginParams;
InitializeTemporaryOriginParams;
@ -169,6 +164,9 @@ parent:
async PQuotaRequest(RequestParams params);
async InitializeStorage()
returns(BoolResponse response);
async ClearStoragesForPrivateBrowsing()
returns(BoolResponse response);

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

@ -28,10 +28,6 @@ struct TemporaryStorageInitializedResponse
bool initialized;
};
struct InitResponse
{
};
struct InitTemporaryStorageResponse
{
};
@ -89,7 +85,6 @@ union RequestResponse
StorageNameResponse;
StorageInitializedResponse;
TemporaryStorageInitializedResponse;
InitResponse;
InitTemporaryStorageResponse;
InitializePersistentOriginResponse;
InitializeTemporaryOriginResponse;

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

@ -300,6 +300,14 @@ class QuotaManager final : public BackgroundThreadObject {
template <typename P>
void CollectPendingOriginsForListing(P aPredicate);
RefPtr<BoolPromise> InitializeStorage();
bool IsStorageInitialized() const {
AssertIsOnOwningThread();
return mStorageInitialized;
}
bool IsStorageInitializedInternal() const {
AssertIsOnIOThread();
return static_cast<bool>(mStorageConnection);
@ -709,6 +717,8 @@ class QuotaManager final : public BackgroundThreadObject {
uint64_t mTemporaryStorageLimit;
uint64_t mTemporaryStorageUsage;
int64_t mNextDirectoryLockId;
uint64_t mShutdownStorageOpCount;
bool mStorageInitialized;
bool mTemporaryStorageInitialized;
bool mCacheUsable;
};

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

@ -528,16 +528,13 @@ QuotaManagerService::Init(nsIQuotaRequest** _retval) {
return NS_ERROR_UNEXPECTED;
}
QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor()));
RefPtr<Request> request = new Request();
InitParams params;
RequestInfo info(request, params);
nsresult rv = InitiateRequest(info);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBackgroundActor->SendInitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
BoolResponsePromiseResolveOrRejectCallback(request));
request.forget(_retval);
return NS_OK;

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

@ -134,7 +134,6 @@ bool Quota::VerifyRequestParams(const RequestParams& aParams) const {
case RequestParams::TStorageNameParams:
case RequestParams::TStorageInitializedParams:
case RequestParams::TTemporaryStorageInitializedParams:
case RequestParams::TInitParams:
case RequestParams::TInitTemporaryStorageParams:
break;
@ -408,9 +407,6 @@ PQuotaRequestParent* Quota::AllocPQuotaRequestParent(
case RequestParams::TTemporaryStorageInitializedParams:
return CreateTemporaryStorageInitializedOp();
case RequestParams::TInitParams:
return CreateInitOp();
case RequestParams::TInitTemporaryStorageParams:
return CreateInitTemporaryStorageOp();
@ -481,6 +477,24 @@ bool Quota::DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) {
return true;
}
mozilla::ipc::IPCResult Quota::RecvInitializeStorage(
InitializeStorageResolver&& aResolver) {
AssertIsOnBackgroundThread();
QM_TRY(MOZ_TO_RESULT(!QuotaManager::IsShuttingDown()),
ResolveBoolResponseAndReturn(aResolver));
QM_TRY_UNWRAP(const NotNull<RefPtr<QuotaManager>> quotaManager,
QuotaManager::GetOrCreate(),
ResolveBoolResponseAndReturn(aResolver));
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
BoolPromiseResolveOrRejectCallback(this, std::move(aResolver)));
return IPC_OK();
}
mozilla::ipc::IPCResult Quota::RecvClearStoragesForPrivateBrowsing(
ClearStoragesForPrivateBrowsingResolver&& aResolver) {
AssertIsOnBackgroundThread();

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

@ -50,6 +50,9 @@ class Quota final : public PQuotaParent {
virtual bool DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override;
virtual mozilla::ipc::IPCResult RecvInitializeStorage(
InitializeStorageResolver&& aResolver) override;
virtual mozilla::ipc::IPCResult RecvClearStoragesForPrivateBrowsing(
ClearStoragesForPrivateBrowsingResolver&& aResolver) override;

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

@ -92,6 +92,24 @@ void QuotaManagerDependencyFixture::ShutdownFixture() {
sBackgroundTarget = nullptr;
}
// static
void QuotaManagerDependencyFixture::InitializeStorage() {
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
}
// static
void QuotaManagerDependencyFixture::StorageInitialized(bool* aResult) {
AutoJSAPI jsapi;
@ -124,6 +142,32 @@ void QuotaManagerDependencyFixture::StorageInitialized(bool* aResult) {
}
}
// static
void QuotaManagerDependencyFixture::IsStorageInitialized(bool* aResult) {
ASSERT_TRUE(aResult);
PerformOnBackgroundThread([aResult]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
*aResult = quotaManager->IsStorageInitialized();
});
}
// static
void QuotaManagerDependencyFixture::AssertStorageIsInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(IsStorageInitialized(&result));
ASSERT_TRUE(result);
}
// static
void QuotaManagerDependencyFixture::AssertStorageIsNotInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(IsStorageInitialized(&result));
ASSERT_FALSE(result);
}
// static
void QuotaManagerDependencyFixture::ShutdownStorage() {
PerformOnBackgroundThread([]() {
@ -173,6 +217,21 @@ void QuotaManagerDependencyFixture::ClearStoragesForOrigin(
[&resolver]() { return resolver->Done(); });
}
// static
OriginMetadata QuotaManagerDependencyFixture::GetTestOriginMetadata() {
return {""_ns,
"example.com"_ns,
"http://example.com"_ns,
"http://example.com"_ns,
/* aIsPrivate */ false,
PERSISTENCE_TYPE_DEFAULT};
}
// static
ClientMetadata QuotaManagerDependencyFixture::GetTestClientMetadata() {
return {GetTestOriginMetadata(), Client::SDB};
}
nsCOMPtr<nsISerialEventTarget> QuotaManagerDependencyFixture::sBackgroundTarget;
} // namespace mozilla::dom::quota::test

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

@ -22,7 +22,11 @@ class QuotaManagerDependencyFixture : public testing::Test {
static void ShutdownFixture();
static void InitializeStorage();
static void StorageInitialized(bool* aResult = nullptr);
static void IsStorageInitialized(bool* aResult);
static void AssertStorageIsInitialized();
static void AssertStorageIsNotInitialized();
static void ShutdownStorage();
static void ClearStoragesForOrigin(const OriginMetadata& aOriginMetadata);
@ -79,6 +83,9 @@ class QuotaManagerDependencyFixture : public testing::Test {
return sBackgroundTarget;
}
static OriginMetadata GetTestOriginMetadata();
static ClientMetadata GetTestClientMetadata();
private:
static nsCOMPtr<nsISerialEventTarget> sBackgroundTarget;
};

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

@ -6,6 +6,8 @@
#include "gtest/gtest.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/dom/quota/DirectoryLock.h"
#include "mozilla/dom/quota/OriginScope.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/gtest/MozAssertions.h"
#include "QuotaManagerDependencyFixture.h"
@ -19,29 +21,611 @@ class TestQuotaManager : public QuotaManagerDependencyFixture {
static void TearDownTestCase() { ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); }
};
// Test simple InitializeStorage.
TEST_F(TestQuotaManager, InitializeStorage_Simple) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization is already ongoing.
TEST_F(TestQuotaManager, InitializeStorage_Ongoing) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(quotaManager->InitializeStorage());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization is already ongoing and
// storage shutdown is scheduled after that.
TEST_F(TestQuotaManager, InitializeStorage_OngoingWithScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(quotaManager->ShutdownStorage());
promises.AppendElement(quotaManager->InitializeStorage());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization is already ongoing and
// an exclusive directory lock is requested after that.
TEST_F(TestQuotaManager, InitializeStorage_OngoingWithExclusiveDirectoryLock) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<UniversalDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLockInternal(Nullable<PersistenceType>(),
OriginScope::FromNull(),
Nullable<Client::Type>(),
/* aExclusive */ true);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&directoryLock](const BoolPromise::ResolveOrRejectValue& aValue) {
// The exclusive directory lock must be released when the first
// storage initialization is finished, otherwise it would endlessly
// block the second storage initialization.
directoryLock = nullptr;
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
promises.AppendElement(directoryLock->Acquire());
promises.AppendElement(quotaManager->InitializeStorage());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization is already ongoing and
// shared client directory locks are requested after that.
// The shared client directory locks don't have to be released in this case.
TEST_F(TestQuotaManager, InitializeStorage_OngoingWithClientDirectoryLocks) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<ClientDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
RefPtr<ClientDirectoryLock> directoryLock2 =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock->Acquire());
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock2->Acquire());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization is already ongoing and
// shared client directory locks are requested after that with storage shutdown
// scheduled in between.
TEST_F(TestQuotaManager,
InitializeStorage_OngoingWithClientDirectoryLocksAndScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<ClientDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
directoryLock->OnInvalidate(
[&directoryLock]() { directoryLock = nullptr; });
RefPtr<ClientDirectoryLock> directoryLock2 =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock->Acquire());
promises.AppendElement(quotaManager->ShutdownStorage());
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock2->Acquire());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization already finished.
TEST_F(TestQuotaManager, InitializeStorage_Finished) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
done = false;
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization already finished but
// storage shutdown has just been scheduled.
TEST_F(TestQuotaManager, InitializeStorage_FinishedWithScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager->InitializeStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->ShutdownStorage());
promises.AppendElement(quotaManager->InitializeStorage());
done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization already finished and
// shared client directory locks are requested immediately after requesting
// storage initialization.
TEST_F(TestQuotaManager, InitializeStorage_FinishedWithClientDirectoryLocks) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<ClientDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock->Acquire());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
RefPtr<ClientDirectoryLock> directoryLock2 =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
promises.Clear();
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock2->Acquire());
done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test InitializeStorage when a storage initialization already finished and
// shared client directory locks are requested immediatelly after requesting
// storage initialization with storage shutdown performed in between.
// The shared client directory lock is released when it gets invalidated by
// storage shutdown which then unblocks the shutdown.
TEST_F(TestQuotaManager,
InitializeStorage_FinishedWithClientDirectoryLocksAndScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<ClientDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
directoryLock->OnInvalidate(
[&directoryLock]() { directoryLock = nullptr; });
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock->Acquire());
bool done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
done = false;
quotaManager->ShutdownStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
RefPtr<ClientDirectoryLock> directoryLock2 =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ false);
promises.Clear();
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(directoryLock2->Acquire());
done = false;
BoolPromise::All(GetCurrentSerialEventTarget(), promises)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
ASSERT_TRUE(false);
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test simple ShutdownStorage.
TEST_F(TestQuotaManager, ShutdownStorage_Simple) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
PerformOnIOThread([]() {
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_NO_FATAL_FAILURE(InitializeStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
bool done = false;
ASSERT_NS_SUCCEEDED(quotaManager->EnsureStorageIsInitializedInternal());
quotaManager->ShutdownStorage()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitializedInternal());
ASSERT_FALSE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
PerformOnIOThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
@ -50,16 +634,11 @@ TEST_F(TestQuotaManager, ShutdownStorage_Simple) {
TEST_F(TestQuotaManager, ShutdownStorage_Ongoing) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
PerformOnIOThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
ASSERT_NO_FATAL_FAILURE(InitializeStorage());
ASSERT_NS_SUCCEEDED(quotaManager->EnsureStorageIsInitializedInternal());
ASSERT_TRUE(quotaManager->IsStorageInitializedInternal());
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
@ -76,6 +655,11 @@ TEST_F(TestQuotaManager, ShutdownStorage_Ongoing) {
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
@ -87,12 +671,7 @@ TEST_F(TestQuotaManager, ShutdownStorage_Ongoing) {
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
PerformOnIOThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
@ -102,16 +681,11 @@ TEST_F(TestQuotaManager, ShutdownStorage_Ongoing) {
TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithScheduledInitialization) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
PerformOnIOThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
ASSERT_NO_FATAL_FAILURE(InitializeStorage());
ASSERT_NS_SUCCEEDED(quotaManager->EnsureStorageIsInitializedInternal());
ASSERT_TRUE(quotaManager->IsStorageInitializedInternal());
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
@ -120,11 +694,7 @@ TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithScheduledInitialization) {
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(quotaManager->ShutdownStorage());
// XXX We have to use ClearPrivateRepository for now because there's no
// dedicated method for initializing storage on the PBackground thread yet.
// ClearPrivateRepository triggers storage initialization before it clear
// the private repository.
promises.AppendElement(quotaManager->ClearPrivateRepository());
promises.AppendElement(quotaManager->InitializeStorage());
promises.AppendElement(quotaManager->ShutdownStorage());
bool done = false;
@ -133,6 +703,11 @@ TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithScheduledInitialization) {
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const CopyableTArray<bool>& aResolveValues) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitialized());
done = true;
},
[&done](nsresult aRejectValue) {
@ -144,12 +719,7 @@ TEST_F(TestQuotaManager, ShutdownStorage_OngoingWithScheduledInitialization) {
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
});
PerformOnIOThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_FALSE(quotaManager->IsStorageInitializedInternal());
});
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}