--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -190,17 +190,17 @@ nsJSUtils::EvaluateString(JSContext* aCx
if (!JS_WrapObject(aCx, scopeChain[i])) {
ok = false;
break;
}
}
if (ok && aOffThreadToken) {
JS::Rooted<JSScript*>
- script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken));
+ script(aCx, JS::FinishOffThreadScript(aCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (script) {
ok = JS_ExecuteScript(aCx, scopeChain, script);
} else {
ok = false;
}
} else if (ok) {
ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -703,18 +703,17 @@ nsScriptLoader::CreateModuleScript(nsMod
masterScriptUpdater.emplace(master->ScriptLoader(),
aRequest->mElement);
}
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> module(cx);
if (aRequest->mWasCompiledOMT) {
- module = JS::FinishOffThreadModule(cx, xpc::GetJSRuntime(),
- aRequest->mOffThreadToken);
+ module = JS::FinishOffThreadModule(cx, aRequest->mOffThreadToken);
aRequest->mOffThreadToken = nullptr;
rv = module ? NS_OK : NS_ERROR_FAILURE;
} else {
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
JS::CompileOptions options(cx);
FillCompileOptionsForRequest(aes, aRequest, global, &options);
@@ -1824,17 +1823,18 @@ nsScriptLoader::ProcessRequest(nsScriptL
}
if (aRequest->mOffThreadToken) {
// The request was parsed off-main-thread, but the result of the off
// thread parse was not actually needed to process the request
// (disappearing window, some other error, ...). Finish the
// request to avoid leaks in the JS engine.
MOZ_ASSERT(!aRequest->IsModuleRequest());
- JS::FinishOffThreadScript(nullptr, xpc::GetJSRuntime(), aRequest->mOffThreadToken);
+ JSContext* cx = JS_GetContext(xpc::GetJSRuntime());
+ JS::CancelOffThreadScript(cx, aRequest->mOffThreadToken);
aRequest->mOffThreadToken = nullptr;
}
// Free any source data.
free(aRequest->mScriptTextBuf);
aRequest->mScriptTextBuf = nullptr;
aRequest->mScriptTextLength = 0;
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -2763,17 +2763,17 @@ NotifyOffThreadScriptCompletedRunnable::
{
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
// Now what? I guess we just leak... this should probably never
// happen.
return NS_ERROR_UNEXPECTED;
}
JSContext* cx = jsapi.cx();
- script = JS::FinishOffThreadScript(cx, JS_GetRuntime(cx), mToken);
+ script = JS::FinishOffThreadScript(cx, mToken);
}
if (!sReceivers) {
// We've already shut down.
return NS_OK;
}
auto index = sReceivers->IndexOf(mReceiver);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4076,36 +4076,54 @@ JS::CompileOffThread(JSContext* cx, cons
const char16_t* chars, size_t length,
OffThreadCompileCallback callback, void* callbackData)
{
MOZ_ASSERT(CanCompileOffThread(cx, options, length));
return StartOffThreadParseScript(cx, options, chars, length, callback, callbackData);
}
JS_PUBLIC_API(JSScript*)
-JS::FinishOffThreadScript(JSContext* maybecx, JSRuntime* rt, void* token)
-{
- MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
- return HelperThreadState().finishScriptParseTask(maybecx, rt, token);
+JS::FinishOffThreadScript(JSContext* cx, void* token)
+{
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+ return HelperThreadState().finishScriptParseTask(cx, token);
+}
+
+JS_PUBLIC_API(void)
+JS::CancelOffThreadScript(JSContext* cx, void* token)
+{
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+ HelperThreadState().cancelParseTask(cx, ParseTaskKind::Script, token);
}
JS_PUBLIC_API(bool)
JS::CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
OffThreadCompileCallback callback, void* callbackData)
{
MOZ_ASSERT(CanCompileOffThread(cx, options, length));
return StartOffThreadParseModule(cx, options, chars, length, callback, callbackData);
}
JS_PUBLIC_API(JSObject*)
-JS::FinishOffThreadModule(JSContext* maybecx, JSRuntime* rt, void* token)
-{
- MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
- return HelperThreadState().finishModuleParseTask(maybecx, rt, token);
+JS::FinishOffThreadModule(JSContext* cx, void* token)
+{
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+ return HelperThreadState().finishModuleParseTask(cx, token);
+}
+
+JS_PUBLIC_API(void)
+JS::CancelOffThreadModule(JSContext* cx, void* token)
+{
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+ HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token);
}
JS_PUBLIC_API(bool)
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
const JS::CompileOptions& options, MutableHandleScript script)
{
return Compile(cx, options, ascii, length, script);
}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4045,41 +4045,48 @@ extern JS_PUBLIC_API(bool)
CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
/*
* Off thread compilation control flow.
*
* After successfully triggering an off thread compile of a script, the
* callback will eventually be invoked with the specified data and a token
* for the compilation. The callback will be invoked while off the main thread,
- * so must ensure that its operations are thread safe. Afterwards,
- * FinishOffThreadScript must be invoked on the main thread to get the result
- * script or nullptr. If maybecx is not specified, the resources will be freed,
- * but no script will be returned.
+ * so must ensure that its operations are thread safe. Afterwards, one of the
+ * following functions must be invoked on the main thread:
+ *
+ * - FinishOffThreadScript, to get the result script (or nullptr on failure).
+ * - CancelOffThreadScript, to free the resources without creating a script.
*
* The characters passed in to CompileOffThread must remain live until the
* callback is invoked, and the resulting script will be rooted until the call
* to FinishOffThreadScript.
*/
extern JS_PUBLIC_API(bool)
CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(JSScript*)
-FinishOffThreadScript(JSContext* maybecx, JSRuntime* rt, void* token);
+FinishOffThreadScript(JSContext* cx, void* token);
+
+extern JS_PUBLIC_API(void)
+CancelOffThreadScript(JSContext* cx, void* token);
extern JS_PUBLIC_API(bool)
CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(JSObject*)
-FinishOffThreadModule(JSContext* maybecx, JSRuntime* rt, void* token);
+FinishOffThreadModule(JSContext* cx, void* token);
+
+extern JS_PUBLIC_API(void)
+CancelOffThreadModule(JSContext* cx, void* token);
/**
* Compile a function with scopeChain plus the global as its scope chain.
* scopeChain must contain objects in the current compartment of cx. The actual
* scope chain used for the function will consist of With wrappers for those
* objects, followed by the current global of the compartment cx is in. This
* global must not be explicitly included in the scope chain.
*/
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3923,27 +3923,26 @@ OffThreadCompileScript(JSContext* cx, un
return true;
}
static bool
runOffThreadScript(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- JSRuntime* rt = cx->runtime();
- if (OffThreadParsingMustWaitForGC(rt))
+ if (OffThreadParsingMustWaitForGC(cx))
gc::FinishGC(cx);
void* token = offThreadState.waitUntilDone(cx, ScriptKind::Script);
if (!token) {
JS_ReportError(cx, "called runOffThreadScript when no compilation is pending");
return false;
}
- RootedScript script(cx, JS::FinishOffThreadScript(cx, rt, token));
+ RootedScript script(cx, JS::FinishOffThreadScript(cx, token));
if (!script)
return false;
return JS_ExecuteScript(cx, script, args.rval());
}
static bool
OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
@@ -4009,27 +4008,26 @@ OffThreadCompileModule(JSContext* cx, un
return true;
}
static bool
FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- JSRuntime* rt = cx->runtime();
- if (OffThreadParsingMustWaitForGC(rt))
+ if (OffThreadParsingMustWaitForGC(cx))
gc::FinishGC(cx);
void* token = offThreadState.waitUntilDone(cx, ScriptKind::Module);
if (!token) {
JS_ReportError(cx, "called finishOffThreadModule when no compilation is pending");
return false;
}
- RootedObject module(cx, JS::FinishOffThreadModule(cx, rt, token));
+ RootedObject module(cx, JS::FinishOffThreadModule(cx, token));
if (!module)
return false;
args.rval().setObject(*module);
return true;
}
struct MOZ_RAII FreeOnReturn
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -341,18 +341,17 @@ js::CancelOffThreadParses(JSRuntime* rt)
GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList();
while (true) {
bool found = false;
for (size_t i = 0; i < finished.length(); i++) {
ParseTask* task = finished[i];
if (task->runtimeMatches(rt)) {
found = true;
AutoUnlockHelperThreadState unlock(lock);
- HelperThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task->kind,
- task);
+ HelperThreadState().cancelParseTask(rt->contextFromMainThread(), task->kind, task);
}
}
if (!found)
break;
}
}
bool
@@ -1114,60 +1113,59 @@ static void
LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
{
// Mark the zone as no longer in use by an ExclusiveContext, and available
// to be collected by the GC.
task->cx->leaveCompartment(task->cx->compartment());
rt->clearUsedByExclusiveThread(task->cx->zone());
}
-JSScript*
-GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind,
- void* token)
+ParseTask*
+GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, void* token)
{
- ScopedJSDeletePtr<ParseTask> parseTask;
-
// The token is a ParseTask* which should be in the finished list.
// Find and remove its entry.
- {
- AutoLockHelperThreadState lock;
- ParseTaskVector& finished = parseFinishedList();
- for (size_t i = 0; i < finished.length(); i++) {
- if (finished[i] == token) {
- parseTask = finished[i];
- remove(finished, &i);
- break;
- }
+
+ AutoLockHelperThreadState lock;
+ ParseTaskVector& finished = parseFinishedList();
+
+ for (size_t i = 0; i < finished.length(); i++) {
+ if (finished[i] == token) {
+ ParseTask* parseTask = finished[i];
+ remove(finished, &i);
+ MOZ_ASSERT(parseTask);
+ MOZ_ASSERT(parseTask->kind == kind);
+ return parseTask;
}
}
- MOZ_ASSERT(parseTask);
- MOZ_ASSERT(parseTask->kind == kind);
+
+ MOZ_CRASH("Invalid ParseTask token");
+}
- if (!maybecx) {
- LeaveParseTaskZone(rt, parseTask);
- return nullptr;
- }
+JSScript*
+GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
+{
+ MOZ_ASSERT(cx->compartment());
- JSContext* cx = maybecx;
- MOZ_ASSERT(cx->compartment());
+ ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
// Make sure we have all the constructors we need for the prototype
// remapping below, since we can't GC while that's happening.
Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
if (!EnsureParserCreatedClasses(cx, kind)) {
- LeaveParseTaskZone(rt, parseTask);
+ LeaveParseTaskZone(cx, parseTask);
return nullptr;
}
mergeParseTaskCompartment(cx, parseTask, global, cx->compartment());
if (!parseTask->finish(cx))
return nullptr;
- RootedScript script(rt, parseTask->script);
+ RootedScript script(cx, parseTask->script);
releaseAssertSameCompartment(cx, script);
// Report out of memory errors eagerly, or errors could be malformed.
if (parseTask->outOfMemory) {
ReportOutOfMemory(cx);
return nullptr;
}
@@ -1189,43 +1187,47 @@ GlobalHelperThreadState::finishParseTask
// The Debugger only needs to be told about the topmost script that was compiled.
Debugger::onNewScript(cx, script);
return script;
}
JSScript*
-GlobalHelperThreadState::finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
+GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
{
- JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Script, token);
+ JSScript* script = finishParseTask(cx, ParseTaskKind::Script, token);
MOZ_ASSERT_IF(script, script->isGlobalCode());
return script;
}
JSObject*
-GlobalHelperThreadState::finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
+GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
{
- JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Module, token);
+ JSScript* script = finishParseTask(cx, ParseTaskKind::Module, token);
if (!script)
return nullptr;
MOZ_ASSERT(script->module());
- if (!maybecx)
- return nullptr;
- JSContext* cx = maybecx;
RootedModuleObject module(cx, script->module());
module->fixScopesAfterCompartmentMerge(cx);
if (!ModuleObject::Freeze(cx, module))
return nullptr;
return module;
}
+void
+GlobalHelperThreadState::cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token)
+{
+ ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
+ LeaveParseTaskZone(cx, parseTask);
+}
+
JSObject*
GlobalObject::getStarGeneratorFunctionPrototype()
{
const Value& v = getReservedSlot(STAR_GENERATOR_FUNCTION_PROTO);
return v.isObject() ? &v.toObject() : nullptr;
}
void
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -96,16 +96,18 @@ class GlobalHelperThreadState
SourceCompressionTaskVector compressionWorklist_;
// Runtimes which have sweeping / allocating work to do.
GCHelperStateVector gcHelperWorklist_;
// GC tasks needing to be done in parallel.
GCParallelTaskVector gcParallelWorklist_;
+ ParseTask* removeFinishedParseTask(ParseTaskKind kind, void* token);
+
public:
size_t maxIonCompilationThreads() const;
size_t maxUnpausedIonCompilationThreads() const;
size_t maxWasmCompilationThreads() const;
size_t maxParseThreads() const;
size_t maxCompressionThreads() const;
size_t maxGCHelperThreads() const;
size_t maxGCParallelThreads() const;
@@ -220,31 +222,33 @@ class GlobalHelperThreadState
MOZ_ASSERT(isLocked());
numWasmFailedJobs++;
}
bool wasmFailed() {
MOZ_ASSERT(isLocked());
return bool(numWasmFailedJobs);
}
- JSScript* finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind, void* token);
+ JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
+ void cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token);
+
void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
Handle<GlobalObject*> global,
JSCompartment* dest);
private:
/*
* Number of wasm jobs that encountered failure for the active module.
* Their parent is logically the main thread, and this number serves for harvesting.
*/
uint32_t numWasmFailedJobs;
public:
- JSScript* finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
- JSObject* finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
+ JSScript* finishScriptParseTask(JSContext* cx, void* token);
+ JSObject* finishModuleParseTask(JSContext* cx, void* token);
bool compressionInProgress(SourceCompressionTask* task);
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss);
bool hasActiveThreads();
void waitForAllThreads();
template <typename T>
bool checkTaskThreadLimit(size_t maxThreads) const;
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -771,19 +771,19 @@ NS_IMETHODIMP
NotifyPrecompilationCompleteRunnable::Run(void)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPrecompiler);
AutoSendObserverNotification notifier(mPrecompiler);
if (mToken) {
- JSRuntime* rt = XPCJSRuntime::Get()->Runtime();
- NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE);
- JS::FinishOffThreadScript(nullptr, rt, mToken);
+ JSContext* cx = XPCJSRuntime::Get()->Context();
+ NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+ JS::CancelOffThreadScript(cx, mToken);
}
return NS_OK;
}
NS_IMETHODIMP
ScriptPrecompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,