mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 13:57:32 +00:00
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:
parent
17a105fe5c
commit
ab304f6276
@ -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.
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user