Backed out 2 changesets (bug 1672431) for causing mochitest failures at IOUtils. CLOSED TREE DONTBUILD

Backed out changeset 1f5a77c484b0 (bug 1672431)
Backed out changeset 65397d9e0ff3 (bug 1672431)
This commit is contained in:
smolnar 2021-02-18 10:20:24 +02:00
parent 17a105fe5c
commit ab304f6276
3 changed files with 230 additions and 398 deletions

View File

@ -4,8 +4,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
interface nsIAsyncShutdownClient;
/**
* IOUtils is a simple, efficient interface for performing file I/O from a
* privileged chrome-only context. All asynchronous I/O tasks are run on
@ -208,12 +206,6 @@ namespace IOUtils {
Promise<boolean> exists(DOMString path);
};
[Exposed=Window]
partial namespace IOUtils {
[Throws]
readonly attribute any profileBeforeChange;
};
/**
* Options to be passed to the |IOUtils.readUTF8| method.
*/

View File

@ -34,7 +34,6 @@
#include "nsIDirectoryEnumerator.h"
#include "nsIFile.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"
#include "nsLocalFile.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
@ -52,14 +51,15 @@
# include "nsSystemInfo.h"
#endif
#define REJECT_IF_SHUTDOWN(_promise) \
do { \
if (sShutdownFinished) { \
IOUtils::RejectJSPromise( \
(_promise), IOError(NS_ERROR_ABORT).WithMessage(SHUTDOWN_ERROR)); \
return (_promise).forget(); \
} \
} while (0)
#define REJECT_IF_SHUTTING_DOWN(aJSPromise) \
do { \
if (sShutdownStarted) { \
(aJSPromise) \
->MaybeRejectWithNotAllowedError( \
"Shutting down and refusing additional I/O tasks"); \
return (aJSPromise).forget(); \
} \
} while (false)
#define REJECT_IF_INIT_PATH_FAILED(_file, _path, _promise) \
do { \
@ -71,9 +71,6 @@
} \
} while (0)
static constexpr auto SHUTDOWN_ERROR =
"IOUtils: Shutting down and refusing additional I/O tasks"_ns;
namespace mozilla::dom {
// static helper functions
@ -152,39 +149,47 @@ MOZ_MUST_USE inline bool ToJSValue(
}
// IOUtils implementation
/* static */
IOUtils::EventQueueMutex IOUtils::sEventQueue{"IOUtils::sEventQueue"};
StaticDataMutex<StaticRefPtr<nsISerialEventTarget>>
IOUtils::sBackgroundEventTarget("sBackgroundEventTarget");
/* static */
Atomic<bool> IOUtils::sShutdownFinished{false};
StaticRefPtr<nsIAsyncShutdownClient> IOUtils::sBarrier;
/* static */
Atomic<bool> IOUtils::sShutdownStarted = Atomic<bool>(false);
/* static */
template <typename OkT, typename Fn>
RefPtr<IOUtils::IOPromise<OkT>> IOUtils::Dispatch(Fn aFunc) {
if (!sShutdownFinished) {
if (auto guard = GetEventQueue(); guard.isSome()) {
auto& eventQueue = guard.ref().ref();
return eventQueue->Dispatch<OkT, Fn>(std::move(aFunc));
}
RefPtr<IOUtils::IOPromise<OkT>> IOUtils::RunOnBackgroundThread(Fn aFunc) {
nsCOMPtr<nsISerialEventTarget> bg = GetBackgroundEventTarget();
if (!bg) {
return IOPromise<OkT>::CreateAndReject(
IOError(NS_ERROR_ABORT)
.WithMessage("Could not dispatch task to background thread"),
__func__);
}
return IOPromise<OkT>::CreateAndReject(
IOError(NS_ERROR_ABORT).WithMessage(SHUTDOWN_ERROR), __func__);
return InvokeAsync(bg, __func__, [func = std::move(aFunc)]() {
Result<OkT, IOError> result = func();
if (result.isErr()) {
return IOPromise<OkT>::CreateAndReject(result.unwrapErr(), __func__);
}
return IOPromise<OkT>::CreateAndResolve(result.unwrap(), __func__);
});
}
/* static */
template <typename OkT, typename Fn>
void IOUtils::DispatchAndResolve(Promise* aPromise, Fn aFunc) {
if (RefPtr<IOPromise<OkT>> p = Dispatch<OkT, Fn>(std::move(aFunc))) {
p->Then(
GetCurrentSerialEventTarget(), __func__,
[promise = RefPtr(aPromise)](OkT&& ok) {
ResolveJSPromise(promise, std::forward<OkT>(ok));
},
[promise = RefPtr(aPromise)](const IOError& err) {
RejectJSPromise(promise, err);
});
}
void IOUtils::RunOnBackgroundThreadAndResolve(Promise* aPromise, Fn aFunc) {
RunOnBackgroundThread<OkT, Fn>(std::move(aFunc))
->Then(
GetCurrentSerialEventTarget(), __func__,
[promise = RefPtr(aPromise)](OkT&& ok) {
ResolveJSPromise(promise, std::forward<OkT>(ok));
},
[promise = RefPtr(aPromise)](const IOError& err) {
RejectJSPromise(promise, err);
});
}
/* static */
@ -196,8 +201,7 @@ already_AddRefed<Promise> IOUtils::Read(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
@ -213,10 +217,11 @@ already_AddRefed<Promise> IOUtils::Read(GlobalObject& aGlobal,
toRead.emplace(aOptions.mMaxBytes.Value());
}
DispatchAndResolve<JsBuffer>(promise, [file = std::move(file), toRead,
decompress = aOptions.mDecompress]() {
return ReadSync(file, toRead, decompress, BufferKind::Uint8Array);
});
RunOnBackgroundThreadAndResolve<JsBuffer>(
promise,
[file = std::move(file), toRead, decompress = aOptions.mDecompress]() {
return ReadSync(file, toRead, decompress, BufferKind::Uint8Array);
});
return promise.forget();
}
@ -230,15 +235,13 @@ already_AddRefed<Promise> IOUtils::ReadUTF8(GlobalObject& aGlobal,
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<JsBuffer>(
RunOnBackgroundThreadAndResolve<JsBuffer>(
promise, [file = std::move(file), decompress = aOptions.mDecompress]() {
return ReadUTF8Sync(file, decompress);
});
return promise.forget();
}
@ -252,56 +255,51 @@ already_AddRefed<Promise> IOUtils::ReadJSON(GlobalObject& aGlobal,
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
RefPtr<IOPromise<JsBuffer>> p =
Dispatch<JsBuffer>([file, decompress = aOptions.mDecompress]() {
return ReadUTF8Sync(file, decompress);
});
RunOnBackgroundThread<JsBuffer>([file, decompress = aOptions.mDecompress]() {
return ReadUTF8Sync(file, decompress);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
[promise, file](JsBuffer&& aBuffer) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(promise->GetGlobalObject()))) {
promise->MaybeRejectWithUnknownError(
"Could not initialize JS API");
return;
}
JSContext* cx = jsapi.cx();
if (p) {
p->Then(
GetCurrentSerialEventTarget(), __func__,
[promise, file](JsBuffer&& aBuffer) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(promise->GetGlobalObject()))) {
promise->MaybeRejectWithUnknownError("Could not initialize JS API");
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSString*> jsonStr(
cx, IOUtils::JsBuffer::IntoString(cx, std::move(aBuffer)));
if (!jsonStr) {
RejectJSPromise(promise, IOError(NS_ERROR_OUT_OF_MEMORY));
return;
}
JS::Rooted<JS::Value> val(cx);
if (!JS_ParseJSON(cx, jsonStr, &val)) {
JS::Rooted<JS::Value> exn(cx);
if (JS_GetPendingException(cx, &exn)) {
JS_ClearPendingException(cx);
promise->MaybeReject(exn);
} else {
RejectJSPromise(
promise,
IOError(NS_ERROR_DOM_UNKNOWN_ERR)
.WithMessage("ParseJSON threw an uncatchable exception "
"while parsing file(%s)",
file->HumanReadablePath().get()));
JS::Rooted<JSString*> jsonStr(
cx, IOUtils::JsBuffer::IntoString(cx, std::move(aBuffer)));
if (!jsonStr) {
RejectJSPromise(promise, IOError(NS_ERROR_OUT_OF_MEMORY));
return;
}
return;
}
JS::Rooted<JS::Value> val(cx);
if (!JS_ParseJSON(cx, jsonStr, &val)) {
JS::Rooted<JS::Value> exn(cx);
if (JS_GetPendingException(cx, &exn)) {
JS_ClearPendingException(cx);
promise->MaybeReject(exn);
} else {
RejectJSPromise(
promise,
IOError(NS_ERROR_DOM_UNKNOWN_ERR)
.WithMessage("ParseJSON threw an uncatchable exception "
"while parsing file(%s)",
file->HumanReadablePath().get()));
}
promise->MaybeResolve(val);
},
[promise](const IOError& aErr) { RejectJSPromise(promise, aErr); });
}
return;
}
promise->MaybeResolve(val);
},
[promise](const IOError& aErr) { RejectJSPromise(promise, aErr); });
return promise.forget();
}
@ -316,8 +314,7 @@ already_AddRefed<Promise> IOUtils::Write(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
@ -336,7 +333,7 @@ already_AddRefed<Promise> IOUtils::Write(GlobalObject& aGlobal,
return promise.forget();
}
DispatchAndResolve<uint32_t>(
RunOnBackgroundThreadAndResolve<uint32_t>(
promise, [file = std::move(file), buf = std::move(*buf),
opts = opts.unwrap()]() { return WriteSync(file, buf, opts); });
@ -353,8 +350,7 @@ already_AddRefed<Promise> IOUtils::WriteUTF8(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
@ -365,7 +361,7 @@ already_AddRefed<Promise> IOUtils::WriteUTF8(GlobalObject& aGlobal,
return promise.forget();
}
DispatchAndResolve<uint32_t>(
RunOnBackgroundThreadAndResolve<uint32_t>(
promise, [file = std::move(file), str = nsCString(aString),
opts = opts.unwrap()]() {
return WriteSync(file, AsBytes(Span(str)), opts);
@ -389,8 +385,7 @@ already_AddRefed<Promise> IOUtils::WriteJSON(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
@ -419,11 +414,12 @@ already_AddRefed<Promise> IOUtils::WriteJSON(GlobalObject& aGlobal,
return promise.forget();
}
DispatchAndResolve<uint32_t>(
RunOnBackgroundThreadAndResolve<uint32_t>(
promise, [file = std::move(file), utf8Str = std::move(utf8Str),
opts = opts.unwrap()]() {
return WriteSync(file, AsBytes(Span(utf8Str)), opts);
});
return promise.forget();
}
@ -437,8 +433,7 @@ already_AddRefed<Promise> IOUtils::Move(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> sourceFile = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(sourceFile, aSourcePath, promise);
@ -446,11 +441,13 @@ already_AddRefed<Promise> IOUtils::Move(GlobalObject& aGlobal,
nsCOMPtr<nsIFile> destFile = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(destFile, aDestPath, promise);
DispatchAndResolve<Ok>(promise, [sourceFile = std::move(sourceFile),
destFile = std::move(destFile),
noOverwrite = aOptions.mNoOverwrite]() {
return MoveSync(sourceFile, destFile, noOverwrite);
});
RunOnBackgroundThreadAndResolve<Ok>(
promise,
[sourceFile = std::move(sourceFile), destFile = std::move(destFile),
noOverwrite = aOptions.mNoOverwrite]() {
return MoveSync(sourceFile, destFile, noOverwrite);
});
return promise.forget();
}
@ -463,17 +460,17 @@ already_AddRefed<Promise> IOUtils::Remove(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<Ok>(
RunOnBackgroundThreadAndResolve<Ok>(
promise, [file = std::move(file), ignoreAbsent = aOptions.mIgnoreAbsent,
recursive = aOptions.mRecursive]() {
return RemoveSync(file, ignoreAbsent, recursive);
});
return promise.forget();
}
@ -486,19 +483,20 @@ already_AddRefed<Promise> IOUtils::MakeDirectory(
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<Ok>(promise, [file = std::move(file),
createAncestors = aOptions.mCreateAncestors,
ignoreExisting = aOptions.mIgnoreExisting,
permissions = aOptions.mPermissions]() {
return MakeDirectorySync(file, createAncestors, ignoreExisting,
permissions);
});
RunOnBackgroundThreadAndResolve<Ok>(
promise,
[file = std::move(file), createAncestors = aOptions.mCreateAncestors,
ignoreExisting = aOptions.mIgnoreExisting,
permissions = aOptions.mPermissions]() {
return MakeDirectorySync(file, createAncestors, ignoreExisting,
permissions);
});
return promise.forget();
}
@ -509,14 +507,14 @@ already_AddRefed<Promise> IOUtils::Stat(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<InternalFileInfo>(
RunOnBackgroundThreadAndResolve<InternalFileInfo>(
promise, [file = std::move(file)]() { return StatSync(file); });
return promise.forget();
}
@ -530,8 +528,7 @@ already_AddRefed<Promise> IOUtils::Copy(GlobalObject& aGlobal,
if (!promise) {
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
REJECT_IF_SHUTTING_DOWN(promise);
nsCOMPtr<nsIFile> sourceFile = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(sourceFile, aSourcePath, promise);
@ -539,12 +536,13 @@ already_AddRefed<Promise> IOUtils::Copy(GlobalObject& aGlobal,
nsCOMPtr<nsIFile> destFile = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(destFile, aDestPath, promise);
DispatchAndResolve<Ok>(
RunOnBackgroundThreadAndResolve<Ok>(
promise,
[sourceFile = std::move(sourceFile), destFile = std::move(destFile),
noOverwrite = aOptions.mNoOverwrite, recursive = aOptions.mRecursive]() {
return CopySync(sourceFile, destFile, noOverwrite, recursive);
});
return promise.forget();
}
@ -558,8 +556,6 @@ already_AddRefed<Promise> IOUtils::Touch(
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
@ -567,9 +563,11 @@ already_AddRefed<Promise> IOUtils::Touch(
if (aModification.WasPassed()) {
newTime = Some(aModification.Value());
}
DispatchAndResolve<int64_t>(promise, [file = std::move(file), newTime]() {
return TouchSync(file, newTime);
});
RunOnBackgroundThreadAndResolve<int64_t>(
promise,
[file = std::move(file), newTime]() { return TouchSync(file, newTime); });
return promise.forget();
}
@ -582,13 +580,12 @@ already_AddRefed<Promise> IOUtils::GetChildren(GlobalObject& aGlobal,
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<nsTArray<nsString>>(
RunOnBackgroundThreadAndResolve<nsTArray<nsString>>(
promise, [file = std::move(file)]() { return GetChildrenSync(file); });
return promise.forget();
}
@ -603,7 +600,8 @@ already_AddRefed<Promise> IOUtils::SetPermissions(GlobalObject& aGlobal,
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
#if defined(XP_UNIX) && !defined(ANDROID)
if (aHonorUmask) {
@ -611,13 +609,11 @@ already_AddRefed<Promise> IOUtils::SetPermissions(GlobalObject& aGlobal,
}
#endif
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<Ok>(
RunOnBackgroundThreadAndResolve<Ok>(
promise, [file = std::move(file), permissions = aPermissions]() {
return SetPermissionsSync(file, permissions);
});
return promise.forget();
}
@ -630,16 +626,75 @@ already_AddRefed<Promise> IOUtils::Exists(GlobalObject& aGlobal,
return nullptr;
}
REJECT_IF_SHUTDOWN(promise);
nsCOMPtr<nsIFile> file = new nsLocalFile();
REJECT_IF_INIT_PATH_FAILED(file, aPath, promise);
DispatchAndResolve<bool>(
RunOnBackgroundThreadAndResolve<bool>(
promise, [file = std::move(file)]() { return ExistsSync(file); });
return promise.forget();
}
/* static */
already_AddRefed<nsISerialEventTarget> IOUtils::GetBackgroundEventTarget() {
if (sShutdownStarted) {
return nullptr;
}
auto lockedBackgroundEventTarget = sBackgroundEventTarget.Lock();
if (!lockedBackgroundEventTarget.ref()) {
nsCOMPtr<nsISerialEventTarget> et;
MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
"IOUtils::BackgroundIOThread", getter_AddRefs(et)));
MOZ_ASSERT(et);
*lockedBackgroundEventTarget = et;
if (NS_IsMainThread()) {
IOUtils::SetShutdownHooks();
} else {
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
__func__, []() { IOUtils::SetShutdownHooks(); });
NS_DispatchToMainThread(runnable.forget());
}
}
return do_AddRef(*lockedBackgroundEventTarget);
}
/* static */
already_AddRefed<nsIAsyncShutdownClient> IOUtils::GetShutdownBarrier() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (!sBarrier) {
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
MOZ_ASSERT(svc);
nsCOMPtr<nsIAsyncShutdownClient> barrier;
nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
NS_ENSURE_SUCCESS(rv, nullptr);
sBarrier = barrier;
}
return do_AddRef(sBarrier);
}
/* static */
void IOUtils::SetShutdownHooks() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
nsCOMPtr<nsIAsyncShutdownBlocker> blocker = new IOUtilsShutdownBlocker();
nsresult rv = barrier->AddBlocker(
blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
u"IOUtils: waiting for pending I/O to finish"_ns);
// Adding a new shutdown blocker should only fail if the current shutdown
// phase has completed. Ensure that we have set our shutdown flag to stop
// accepting new I/O tasks in this case.
if (NS_FAILED(rv)) {
sShutdownStarted = true;
}
NS_ENSURE_SUCCESS_VOID(rv);
}
/* static */
already_AddRefed<Promise> IOUtils::CreateJSPromise(GlobalObject& aGlobal) {
ErrorResult er;
@ -1363,118 +1418,6 @@ Result<bool, IOUtils::IOError> IOUtils::ExistsSync(nsIFile* aFile) {
return exists;
}
/* static */
void IOUtils::GetProfileBeforeChange(GlobalObject& aGlobal,
JS::MutableHandle<JS::Value> aClient,
ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread());
if (auto guard = GetEventQueue()) {
MOZ_RELEASE_ASSERT(
!sShutdownFinished,
"GetEventQueue() only returns Some() when !sShutdownFinished");
RefPtr<nsIAsyncShutdownClient> client =
guard.ref().ref()->ProfileBeforeChangeClient();
MOZ_RELEASE_ASSERT(client);
if (nsresult rv = client->GetJsclient(aClient); NS_FAILED(rv)) {
aRv.ThrowAbortError(
"Could not get jsclient for IOUtils shutdown blocker");
}
return;
}
aRv.ThrowAbortError(
"IOUtils: profileBeforeChange phase has already finished");
}
/* static */
Maybe<IOUtils::EventQueueMutex::AutoLock> IOUtils::GetEventQueue() {
auto eventQueue = sEventQueue.Lock();
if (sShutdownFinished) {
return Nothing{};
}
if (!eventQueue.ref()) {
eventQueue.ref() = new IOUtils::EventQueue();
if (NS_IsMainThread()) {
eventQueue.ref()->SetShutdownHooks();
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction(__func__, []() {
auto guard = IOUtils::sEventQueue.Lock();
MOZ_RELEASE_ASSERT(guard.ref());
guard.ref()->SetShutdownHooks();
}));
}
}
return Some(std::move(eventQueue));
}
IOUtils::EventQueue::EventQueue() {
MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
"IOUtils::EventQueue", getter_AddRefs(mBackgroundEventTarget)));
MOZ_RELEASE_ASSERT(mBackgroundEventTarget);
}
nsresult IOUtils::EventQueue::SetShutdownHooks() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!mProfileBeforeChangeBarrier);
MOZ_RELEASE_ASSERT(!IOUtils::sShutdownFinished);
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
MOZ_RELEASE_ASSERT(svc);
MOZ_TRY(svc->MakeBarrier(
u"IOUtils: waiting for profileBeforeChange IO to complete"_ns,
getter_AddRefs(mProfileBeforeChangeBarrier)));
nsCOMPtr<nsIAsyncShutdownClient> profileBeforeChange;
MOZ_TRY(svc->GetProfileBeforeChange(getter_AddRefs(profileBeforeChange)));
MOZ_RELEASE_ASSERT(profileBeforeChange);
nsCOMPtr<nsIAsyncShutdownBlocker> blocker = new IOUtilsShutdownBlocker();
nsresult rv = profileBeforeChange->AddBlocker(
blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
u"IOUtils::SetShutdownHooks"_ns);
if (NS_FAILED(rv)) {
IOUtils::sShutdownFinished = true;
}
return NS_OK;
}
template <typename OkT, typename Fn>
RefPtr<IOUtils::IOPromise<OkT>> IOUtils::EventQueue::Dispatch(Fn aFunc) {
MOZ_RELEASE_ASSERT(mBackgroundEventTarget);
MOZ_RELEASE_ASSERT(!IOUtils::sShutdownFinished);
return InvokeAsync(
mBackgroundEventTarget, __func__, [func = std::move(aFunc)]() {
Result<OkT, IOError> result = func();
if (result.isErr()) {
return IOPromise<OkT>::CreateAndReject(result.unwrapErr(), __func__);
}
return IOPromise<OkT>::CreateAndResolve(result.unwrap(), __func__);
});
};
already_AddRefed<nsIAsyncShutdownClient>
IOUtils::EventQueue::ProfileBeforeChangeClient() {
nsCOMPtr<nsIAsyncShutdownClient> profileBeforeChange;
MOZ_ALWAYS_SUCCEEDS(mProfileBeforeChangeBarrier->GetClient(
getter_AddRefs(profileBeforeChange)));
return profileBeforeChange.forget();
}
already_AddRefed<nsIAsyncShutdownBarrier>
IOUtils::EventQueue::ProfileBeforeChangeBarrier() {
return do_AddRef(mProfileBeforeChangeBarrier);
}
/* static */
Result<nsTArray<uint8_t>, IOUtils::IOError> IOUtils::MozLZ4::Compress(
Span<const uint8_t> aUncompressed) {
@ -1561,8 +1504,7 @@ Result<IOUtils::JsBuffer, IOUtils::IOError> IOUtils::MozLZ4::Decompress(
return decompressed;
}
NS_IMPL_ISUPPORTS(IOUtilsShutdownBlocker, nsIAsyncShutdownBlocker,
nsIAsyncShutdownCompletionCallback);
NS_IMPL_ISUPPORTS(IOUtilsShutdownBlocker, nsIAsyncShutdownBlocker);
NS_IMETHODIMP IOUtilsShutdownBlocker::GetName(nsAString& aName) {
aName = u"IOUtils Blocker"_ns;
@ -1571,53 +1513,30 @@ NS_IMETHODIMP IOUtilsShutdownBlocker::GetName(nsAString& aName) {
NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
nsIAsyncShutdownClient* aBarrierClient) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsISerialEventTarget> et = IOUtils::GetBackgroundEventTarget();
auto eventQueue = IOUtils::sEventQueue.Lock();
MOZ_RELEASE_ASSERT(!IOUtils::sShutdownFinished);
MOZ_RELEASE_ASSERT(eventQueue.ref());
IOUtils::sShutdownStarted = true;
mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
"IOUtilsShutdownBlocker::mParentClient", aBarrierClient);
nsCOMPtr<nsIAsyncShutdownBarrier> barrier =
eventQueue.ref()->ProfileBeforeChangeBarrier();
if (NS_WARN_IF(NS_FAILED(barrier->Wait(this)))) {
Unused << Done(*eventQueue.ref());
if (!IOUtils::sBarrier) {
return NS_ERROR_NULL_POINTER;
}
return NS_OK;
}
NS_IMETHODIMP IOUtilsShutdownBlocker::Done() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> backgroundRunnable =
NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() {
nsCOMPtr<nsIRunnable> mainThreadRunnable =
NS_NewRunnableFunction(__func__, [self = RefPtr(self)]() {
IOUtils::sBarrier->RemoveBlocker(self);
auto eventQueue = IOUtils::sEventQueue.Lock();
MOZ_RELEASE_ASSERT(!IOUtils::sShutdownFinished);
MOZ_RELEASE_ASSERT(eventQueue.ref());
auto lockedBackgroundET = IOUtils::sBackgroundEventTarget.Lock();
*lockedBackgroundET = nullptr;
IOUtils::sBarrier = nullptr;
});
nsresult rv = NS_DispatchToMainThread(mainThreadRunnable.forget());
NS_ENSURE_SUCCESS_VOID(rv);
});
return Done(*eventQueue.ref());
}
nsresult IOUtilsShutdownBlocker::Done(IOUtils::EventQueue& aEventQueue) {
// This method is called once we have served all shutdown clients. Now we
// flush the remaining IO queue and forbid additional IO requests.
aEventQueue.Dispatch<Ok>([]() { return Ok{}; })
->Then(GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this)]() {
if (self->mParentClient) {
Unused << NS_WARN_IF(
NS_FAILED(self->mParentClient->RemoveBlocker(self)));
self->mParentClient = nullptr;
auto eventQueue = IOUtils::sEventQueue.Lock();
MOZ_RELEASE_ASSERT(eventQueue.ref());
eventQueue.ref() = nullptr;
}
});
aEventQueue.SetShutdownFinished();
return NS_OK;
return et->Dispatch(backgroundRunnable.forget(),
nsIEventTarget::DISPATCH_NORMAL);
}
NS_IMETHODIMP IOUtilsShutdownBlocker::GetState(nsIPropertyBag** aState) {

View File

@ -14,14 +14,12 @@
#include "mozilla/DataMutex.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Result.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/IOUtilsBinding.h"
#include "mozilla/dom/TypedArray.h"
#include "nsIAsyncShutdown.h"
#include "nsISerialEventTarget.h"
#include "nsPrintfCString.h"
#include "nsProxyRelease.h"
#include "nsString.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
@ -121,10 +119,6 @@ class IOUtils final {
static already_AddRefed<Promise> Exists(GlobalObject& aGlobal,
const nsAString& aPath);
static void GetProfileBeforeChange(GlobalObject& aGlobal,
JS::MutableHandle<JS::Value>,
ErrorResult& aRv);
class JsBuffer;
/**
@ -148,33 +142,27 @@ class IOUtils final {
struct InternalFileInfo;
struct InternalWriteOpts;
class MozLZ4;
class EventQueue;
/**
* Dispatch a task on the event queue.
*
* If shutdown has finished, the promise will be rejected appropriately.
*
* @param aFunc The task to run.
*
* @return The MozPromise for chaining effects on the task that was run.
*
* If the event queue is already shut down, a reject promise is
* returned.
*/
template <typename OkT, typename Fn>
static RefPtr<IOPromise<OkT>> Dispatch(Fn aFunc);
static StaticDataMutex<StaticRefPtr<nsISerialEventTarget>>
sBackgroundEventTarget;
static StaticRefPtr<nsIAsyncShutdownClient> sBarrier;
static Atomic<bool> sShutdownStarted;
template <typename OkT, typename Fn, typename... Args>
static RefPtr<IOUtils::IOPromise<OkT>> InvokeToIOPromise(Fn aFunc,
Args... aArgs);
static already_AddRefed<nsIAsyncShutdownClient> GetShutdownBarrier();
static already_AddRefed<nsISerialEventTarget> GetBackgroundEventTarget();
static void SetShutdownHooks();
/**
* Dispatch a task on the event queue and resolve or reject the associated
* promise based on the result.
*
* @param aPromise The promise corresponding to the task running on the event
* queue.
* @param aFunc The task to run.
*/
template <typename OkT, typename Fn>
static void DispatchAndResolve(Promise* aPromise, Fn aFunc);
static RefPtr<IOPromise<OkT>> RunOnBackgroundThread(Fn aFunc);
template <typename OkT, typename Fn>
static void RunOnBackgroundThreadAndResolve(Promise* aPromise, Fn aFunc);
/**
* Creates a new JS Promise.
@ -381,67 +369,6 @@ class IOUtils final {
* @return Whether or not the file exists.
*/
static Result<bool, IOError> ExistsSync(nsIFile* aFile);
using EventQueueMutex = StaticDataMutex<StaticAutoPtr<EventQueue>>;
/**
* Lock the event queue and return a handle. If shutdown has not yet
* finished, the event queue will be constructed if necessary.
*
*
* @returns A lock handle to the event queue mutex, which can be used to
* retrieve the EventQueue. If |Some| is returned, the event queue is
* guaranteed to be non-null.
* If shutdown has finished, |Nothing| will be returned instead.
*/
static Maybe<EventQueueMutex::AutoLock> GetEventQueue();
/**
* Our event queue.
*
* This mutex also guards writing to sShutdownFinished.
*/
static EventQueueMutex sEventQueue;
static Atomic<bool> sShutdownFinished;
};
/**
* The IOUtils event queue.
*/
class IOUtils::EventQueue final {
public:
EventQueue();
EventQueue(const EventQueue&) = delete;
EventQueue(EventQueue&&) = delete;
EventQueue& operator=(const EventQueue&) = delete;
EventQueue& operator=(EventQueue&&) = delete;
/**
* Set up shutdown hooks to free our internals at shutdown.
*
* NB: Must be called on main thread.
*/
nsresult SetShutdownHooks();
template <typename OkT, typename Fn>
RefPtr<IOPromise<OkT>> Dispatch(Fn aFunc);
/**
* Set the shutdown started flag, preventing further jobs from being
* scheduled.
*/
void SetShutdownFinished() {
MOZ_ASSERT(!IOUtils::sShutdownFinished);
IOUtils::sShutdownFinished = true;
}
already_AddRefed<nsIAsyncShutdownClient> ProfileBeforeChangeClient();
already_AddRefed<nsIAsyncShutdownBarrier> ProfileBeforeChangeBarrier();
private:
nsCOMPtr<nsISerialEventTarget> mBackgroundEventTarget = nullptr;
nsCOMPtr<nsIAsyncShutdownBarrier> mProfileBeforeChangeBarrier = nullptr;
};
/**
@ -554,19 +481,13 @@ class IOUtils::MozLZ4 {
Span<const uint8_t> aFileContents, IOUtils::BufferKind);
};
class IOUtilsShutdownBlocker : public nsIAsyncShutdownBlocker,
public nsIAsyncShutdownCompletionCallback {
class IOUtilsShutdownBlocker : public nsIAsyncShutdownBlocker {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
private:
virtual ~IOUtilsShutdownBlocker() = default;
nsresult Done(IOUtils::EventQueue& aEventQueue);
nsMainThreadPtrHandle<nsIAsyncShutdownClient> mParentClient;
};
/**