Bug 1749504 - Introduce a new method QuotaManager::OpenStorageDirectory; r=dom-storage-reviewers,jstutte

Quota manager origin operations and IndexedDB database maintenance currently
call QuotaManager::CreateDirectoryLockInternal and DirectoryLock::Acquire to
acquire a universal directory lock. Once that's all successfully done, they
bounce to the QuotaManager IO thread where they synchronously call
QuotaManager::EnsureStorageIsInitializedInternal.
The new QuotaManager::OpenStorageDirectory method wraps all this complexity, so
consumers can just obtain a universal directory lock when all required storage
initialization is done.

Changes done in this patch:
- added QuotaManager::OpenStorageDirectory
- added tests for the new method

Differential Revision: https://phabricator.services.mozilla.com/D186134
This commit is contained in:
Jan Varga 2023-09-17 06:37:28 +00:00
Родитель fb80484f6c
Коммит 9fd6f9cf81
4 изменённых файлов: 493 добавлений и 0 удалений

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

@ -4993,6 +4993,79 @@ nsresult QuotaManager::EnsureStorageIsInitializedInternal() {
"dom::quota::FirstInitializationAttempt::Storage"_ns, innerFunc);
}
RefPtr<UniversalDirectoryLockPromise> QuotaManager::OpenStorageDirectory(
const Nullable<PersistenceType>& aPersistenceType,
const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType,
bool aExclusive,
Maybe<RefPtr<UniversalDirectoryLock>&> aPendingDirectoryLockOut) {
AssertIsOnOwningThread();
RefPtr<UniversalDirectoryLock> storageDirectoryLock;
RefPtr<BoolPromise> storageDirectoryLockPromise;
if (mStorageInitialized && !mShutdownStorageOpCount) {
storageDirectoryLockPromise = BoolPromise::CreateAndResolve(true, __func__);
} else {
storageDirectoryLock = CreateDirectoryLockInternal(
Nullable<PersistenceType>(), OriginScope::FromNull(),
Nullable<Client::Type>(),
/* aExclusive */ false);
storageDirectoryLockPromise = storageDirectoryLock->Acquire();
}
RefPtr<UniversalDirectoryLock> universalDirectoryLock =
CreateDirectoryLockInternal(aPersistenceType, aOriginScope, aClientType,
aExclusive);
RefPtr<BoolPromise> universalDirectoryLockPromise =
universalDirectoryLock->Acquire();
if (aPendingDirectoryLockOut.isSome()) {
aPendingDirectoryLockOut.ref() = universalDirectoryLock;
}
return storageDirectoryLockPromise
->Then(GetCurrentSerialEventTarget(), __func__,
[self = RefPtr(this),
storageDirectoryLock = std::move(storageDirectoryLock)](
const BoolPromise::ResolveOrRejectValue& aValue) mutable {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
if (!storageDirectoryLock) {
return BoolPromise::CreateAndResolve(true, __func__);
}
return self->InitializeStorage(std::move(storageDirectoryLock));
})
->Then(GetCurrentSerialEventTarget(), __func__,
[universalDirectoryLockPromise =
std::move(universalDirectoryLockPromise)](
const BoolPromise::ResolveOrRejectValue& aValue) mutable {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return std::move(universalDirectoryLockPromise);
})
->Then(GetCurrentSerialEventTarget(), __func__,
[universalDirectoryLock = std::move(universalDirectoryLock)](
const BoolPromise::ResolveOrRejectValue& aValue) mutable {
if (aValue.IsReject()) {
return UniversalDirectoryLockPromise::CreateAndReject(
aValue.RejectValue(), __func__);
}
return UniversalDirectoryLockPromise::CreateAndResolve(
std::move(universalDirectoryLock), __func__);
});
}
RefPtr<ClientDirectoryLockPromise> QuotaManager::OpenClientDirectory(
const ClientMetadata& aClientMetadata,
Maybe<RefPtr<ClientDirectoryLock>&> aPendingDirectoryLockOut) {

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

@ -50,9 +50,12 @@ using BoolResponseResolver = std::function<void(const BoolResponse&)>;
namespace dom::quota {
class ClientDirectoryLock;
class UniversalDirectoryLock;
using ClientDirectoryLockPromise =
MozPromise<RefPtr<ClientDirectoryLock>, nsresult, true>;
using UniversalDirectoryLockPromise =
MozPromise<RefPtr<UniversalDirectoryLock>, nsresult, true>;
} // namespace dom::quota

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

@ -258,6 +258,13 @@ class QuotaManager final : public BackgroundThreadObject {
Result<OriginMetadata, nsresult> GetOriginMetadata(nsIFile* aDirectory);
RefPtr<UniversalDirectoryLockPromise> OpenStorageDirectory(
const Nullable<PersistenceType>& aPersistenceType,
const OriginScope& aOriginScope,
const Nullable<Client::Type>& aClientType, bool aExclusive,
Maybe<RefPtr<UniversalDirectoryLock>&> aPendingDirectoryLockOut =
Nothing());
// This is the main entry point into the QuotaManager API.
// Any storage API implementation (quota client) that participates in
// centralized quota and storage handling should call this method to get

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

@ -21,6 +21,414 @@ class TestQuotaManager : public QuotaManagerDependencyFixture {
static void TearDownTestCase() { ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); }
};
// Test OpenStorageDirectory when an opening of the storage directory is
// already ongoing and storage shutdown is scheduled after that.
TEST_F(TestQuotaManager, OpenStorageDirectory_OngoingWithScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
RefPtr<UniversalDirectoryLock> directoryLock;
nsTArray<RefPtr<BoolPromise>> promises;
promises.AppendElement(
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(GetCurrentSerialEventTarget(), __func__,
[&directoryLock](
UniversalDirectoryLockPromise::ResolveOrRejectValue&&
aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
[&aValue]() { ASSERT_TRUE(aValue.ResolveValue()); }();
directoryLock = std::move(aValue.ResolveValue());
return BoolPromise::CreateAndResolve(true, __func__);
})
->Then(quotaManager->IOThread(), __func__,
[](const BoolPromise::ResolveOrRejectValue& aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
[]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(
quotaManager->IsStorageInitializedInternal());
}();
return BoolPromise::CreateAndResolve(true, __func__);
})
->Then(GetCurrentSerialEventTarget(), __func__,
[&directoryLock](
const BoolPromise::ResolveOrRejectValue& aValue) {
directoryLock = nullptr;
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
promises.AppendElement(quotaManager->ShutdownStorage());
promises.AppendElement(
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(GetCurrentSerialEventTarget(), __func__,
[](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
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 OpenStorageDirectory when an opening of the storage directory is
// already ongoing and an exclusive directory lock is requested after that.
TEST_F(TestQuotaManager,
OpenStorageDirectory_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
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&directoryLock](
const UniversalDirectoryLockPromise::ResolveOrRejectValue&
aValue) {
directoryLock = nullptr;
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
promises.AppendElement(directoryLock->Acquire());
promises.AppendElement(
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(GetCurrentSerialEventTarget(), __func__,
[](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
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 OpenStorageDirectory when an opening of the storage directory already
// finished.
TEST_F(TestQuotaManager, OpenStorageDirectory_Finished) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const UniversalDirectoryLockPromise::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
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const UniversalDirectoryLockPromise::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 OpenStorageDirectory when an opening of the storage directory already
// finished but storage shutdown has just been scheduled.
TEST_F(TestQuotaManager, OpenStorageDirectory_FinishedWithScheduledShutdown) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const UniversalDirectoryLockPromise::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
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(GetCurrentSerialEventTarget(), __func__,
[](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
aValue) {
if (aValue.IsReject()) {
return BoolPromise::CreateAndReject(aValue.RejectValue(),
__func__);
}
return BoolPromise::CreateAndResolve(true, __func__);
}));
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 OpenStorageDirectory when an opening of the storage directory already
// finished and an exclusive client directory lock for a non-overlapping
// origin is acquired in between.
TEST_F(TestQuotaManager,
OpenStorageDirectory_FinishedWithExclusiveClientDirectoryLock) {
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(AssertStorageIsNotInitialized());
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
bool done = false;
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const UniversalDirectoryLockPromise::ResolveOrRejectValue&
aValue) {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->IsStorageInitialized());
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
RefPtr<ClientDirectoryLock> directoryLock =
quotaManager->CreateDirectoryLock(GetTestClientMetadata(),
/* aExclusive */ true);
done = false;
directoryLock->Acquire()->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const BoolPromise::ResolveOrRejectValue& aValue) {
done = true;
});
SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; });
done = false;
quotaManager
->OpenStorageDirectory(
Nullable<PersistenceType>(PERSISTENCE_TYPE_PERSISTENT),
OriginScope::FromNull(), Nullable<Client::Type>(),
/* aExclusive */ false)
->Then(
GetCurrentSerialEventTarget(), __func__,
[&done](const UniversalDirectoryLockPromise::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 OpenClientDirctory when an opening of a client directory is already
// ongoing and storage shutdown is scheduled after that.
TEST_F(TestQuotaManager, OpenClientDirectory_OngoingWithScheduledShutdown) {
@ -47,6 +455,8 @@ TEST_F(TestQuotaManager, OpenClientDirectory_OngoingWithScheduledShutdown) {
__func__);
}
[&aValue]() { ASSERT_TRUE(aValue.ResolveValue()); }();
directoryLock = std::move(aValue.ResolveValue());
return BoolPromise::CreateAndResolve(true, __func__);