mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-28 01:57:30 +09:00
(not to be confused with Basilisk-Dev's implementation) Backport Bugzilla 1379814 and 1431353.
This commit is contained in:
parent
dd7052503e
commit
b0cad39d1a
3 changed files with 283 additions and 128 deletions
|
|
@ -16,6 +16,7 @@
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
#include "mozilla/SizePrintfMacros.h"
|
#include "mozilla/SizePrintfMacros.h"
|
||||||
#include "mozilla/Sprintf.h"
|
#include "mozilla/Sprintf.h"
|
||||||
|
#include "mozilla/Variant.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
|
@ -173,90 +174,213 @@ enum class ScriptKind
|
||||||
Module
|
Module
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mozilla::Atomic<int32_t> gOffThreadJobSerial(1);
|
||||||
|
|
||||||
|
class OffThreadJob;
|
||||||
|
|
||||||
class OffThreadState {
|
class OffThreadState {
|
||||||
enum State {
|
enum State {
|
||||||
IDLE, /* ready to work; no token, no source */
|
RUNNING,
|
||||||
COMPILING, /* working; no token, have source */
|
DONE,
|
||||||
DONE /* compilation done: have token and source */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OffThreadState()
|
OffThreadState()
|
||||||
: monitor(mutexid::ShellOffThreadState),
|
: monitor(mutexid::ShellOffThreadState)
|
||||||
state(IDLE),
|
{}
|
||||||
token(),
|
|
||||||
source(nullptr)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
bool startIfIdle(JSContext* cx, ScriptKind kind,
|
~OffThreadState()
|
||||||
ScopedJSFreePtr<char16_t>& newSource)
|
|
||||||
{
|
{
|
||||||
AutoLockMonitor alm(monitor);
|
MOZ_ASSERT(jobs.empty());
|
||||||
if (state != IDLE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MOZ_ASSERT(!token);
|
|
||||||
|
|
||||||
source = newSource.forget();
|
|
||||||
|
|
||||||
scriptKind = kind;
|
|
||||||
state = COMPILING;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void abandon(JSContext* cx) {
|
using Source = JS::UniqueTwoByteChars;
|
||||||
AutoLockMonitor alm(monitor);
|
|
||||||
MOZ_ASSERT(state == COMPILING);
|
|
||||||
MOZ_ASSERT(!token);
|
|
||||||
MOZ_ASSERT(source);
|
|
||||||
|
|
||||||
js_free(source);
|
OffThreadJob* newJob(JSContext* cx, ScriptKind kind, Source&& source);
|
||||||
source = nullptr;
|
OffThreadJob* getSingleJob(JSContext* cx, ScriptKind kind);
|
||||||
|
OffThreadJob* lookupJobByID(JSContext* cx, ScriptKind kind, int32_t id);
|
||||||
|
OffThreadJob* lookupJobForArgs(JSContext* cx, ScriptKind kind, const CallArgs& args,
|
||||||
|
size_t arg);
|
||||||
|
void deleteJob(JSContext* cx, OffThreadJob* job);
|
||||||
|
void cancelAll(JSContext* cx);
|
||||||
|
|
||||||
state = IDLE;
|
|
||||||
|
private:
|
||||||
|
friend class OffThreadJob;
|
||||||
|
|
||||||
|
Monitor monitor;
|
||||||
|
Vector<OffThreadJob*, 0, SystemAllocPolicy> jobs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OffThreadJob {
|
||||||
|
public:
|
||||||
|
using Source = OffThreadState::Source;
|
||||||
|
|
||||||
|
OffThreadJob(Monitor& monitor, ScriptKind kind, Source&& source)
|
||||||
|
: id(gOffThreadJobSerial++),
|
||||||
|
kind(kind),
|
||||||
|
monitor(monitor),
|
||||||
|
state(RUNNING),
|
||||||
|
token(nullptr),
|
||||||
|
source(Move(source))
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
|
||||||
|
}
|
||||||
|
|
||||||
|
~OffThreadJob() {
|
||||||
|
MOZ_ASSERT(state != RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDone(void* newToken) {
|
void markDone(void* newToken) {
|
||||||
AutoLockMonitor alm(monitor);
|
AutoLockMonitor alm(monitor);
|
||||||
MOZ_ASSERT(state == COMPILING);
|
MOZ_ASSERT(state == RUNNING);
|
||||||
MOZ_ASSERT(!token);
|
MOZ_ASSERT(!token);
|
||||||
MOZ_ASSERT(source);
|
|
||||||
MOZ_ASSERT(newToken);
|
MOZ_ASSERT(newToken);
|
||||||
|
|
||||||
token = newToken;
|
token = newToken;
|
||||||
state = DONE;
|
state = DONE;
|
||||||
alm.notify();
|
alm.notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* waitUntilDone(JSContext* cx, ScriptKind kind) {
|
void* waitUntilDone(JSContext* cx) {
|
||||||
AutoLockMonitor alm(monitor);
|
AutoLockMonitor alm(monitor);
|
||||||
if (state == IDLE || scriptKind != kind)
|
MOZ_ASSERT(state != RUNNING || token == nullptr);
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (state == COMPILING) {
|
while (state != DONE)
|
||||||
while (state != DONE)
|
alm.wait();
|
||||||
alm.wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(source);
|
|
||||||
js_free(source);
|
|
||||||
source = nullptr;
|
|
||||||
|
|
||||||
MOZ_ASSERT(token);
|
MOZ_ASSERT(token);
|
||||||
void* holdToken = token;
|
return token;
|
||||||
token = nullptr;
|
|
||||||
state = IDLE;
|
|
||||||
return holdToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char16_t* sourceChars() const {
|
||||||
|
return source.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t id;
|
||||||
|
const ScriptKind kind;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Monitor monitor;
|
Monitor& monitor;
|
||||||
ScriptKind scriptKind;
|
|
||||||
State state;
|
State state;
|
||||||
void* token;
|
void* token;
|
||||||
char16_t* source;
|
Source source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OffThreadJob*
|
||||||
|
OffThreadState::newJob(JSContext* cx, ScriptKind kind, Source&& source)
|
||||||
|
{
|
||||||
|
UniquePtr<OffThreadJob> job(cx->new_<OffThreadJob>(monitor, kind, Move(source)));
|
||||||
|
if (!job)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
AutoLockMonitor alm(monitor);
|
||||||
|
if (!jobs.append(job.get())) {
|
||||||
|
JS_ReportErrorASCII(cx, "OOM adding off-thread job");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return job.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
OffThreadJob*
|
||||||
|
OffThreadState::getSingleJob(JSContext* cx, ScriptKind kind)
|
||||||
|
{
|
||||||
|
AutoLockMonitor alm(monitor);
|
||||||
|
if (jobs.empty()) {
|
||||||
|
JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jobs.length() > 1) {
|
||||||
|
JS_ReportErrorASCII(cx, "Multiple off-thread jobs are pending: must specify job ID");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OffThreadJob* job = jobs[0];
|
||||||
|
if (job->kind != kind) {
|
||||||
|
JS_ReportErrorASCII(cx, "Off-thread job is the wrong kind");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
OffThreadJob*
|
||||||
|
OffThreadState::lookupJobByID(JSContext* cx, ScriptKind kind, int32_t id)
|
||||||
|
{
|
||||||
|
if (id <= 0) {
|
||||||
|
JS_ReportErrorASCII(cx, "Bad off-thread job ID");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoLockMonitor alm(monitor);
|
||||||
|
if (jobs.empty()) {
|
||||||
|
JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OffThreadJob* job = nullptr;
|
||||||
|
for (auto someJob : jobs) {
|
||||||
|
if (someJob->id == id) {
|
||||||
|
job = someJob;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!job) {
|
||||||
|
JS_ReportErrorASCII(cx, "Off-thread job not found");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job->kind != kind) {
|
||||||
|
JS_ReportErrorASCII(cx, "Off-thread job is the wrong kind");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
OffThreadJob*
|
||||||
|
OffThreadState::lookupJobForArgs(JSContext* cx, ScriptKind kind, const CallArgs& args,
|
||||||
|
size_t arg)
|
||||||
|
{
|
||||||
|
if (args.length() <= arg)
|
||||||
|
return getSingleJob(cx, kind);
|
||||||
|
|
||||||
|
int32_t id = 0;
|
||||||
|
RootedValue value(cx, args[arg]);
|
||||||
|
if (!ToInt32(cx, value, &id))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return lookupJobByID(cx, kind, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OffThreadState::deleteJob(JSContext* cx, OffThreadJob* job)
|
||||||
|
{
|
||||||
|
AutoLockMonitor alm(monitor);
|
||||||
|
for (size_t i = 0; i < jobs.length(); i++) {
|
||||||
|
if (jobs[i] == job) {
|
||||||
|
jobs.erase(&jobs[i]);
|
||||||
|
js_delete(job);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("Off-thread job not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OffThreadState::cancelAll(JSContext* cx)
|
||||||
|
{
|
||||||
|
AutoLockMonitor alm(monitor);
|
||||||
|
while (!jobs.empty())
|
||||||
|
js_delete(jobs.popCopy());
|
||||||
|
}
|
||||||
|
|
||||||
// Per-context shell state.
|
// Per-context shell state.
|
||||||
struct ShellContext
|
struct ShellContext
|
||||||
{
|
{
|
||||||
|
|
@ -3437,6 +3561,8 @@ WorkerMain(void* arg)
|
||||||
JS::SetEnqueuePromiseJobCallback(cx, nullptr);
|
JS::SetEnqueuePromiseJobCallback(cx, nullptr);
|
||||||
sc->jobQueue.reset();
|
sc->jobQueue.reset();
|
||||||
|
|
||||||
|
sc->offThreadState.cancelAll(cx);
|
||||||
|
|
||||||
KillWatchdog(cx);
|
KillWatchdog(cx);
|
||||||
|
|
||||||
JS_DestroyContext(cx);
|
JS_DestroyContext(cx);
|
||||||
|
|
@ -4424,8 +4550,8 @@ SyntaxParse(JSContext* cx, unsigned argc, Value* vp)
|
||||||
static void
|
static void
|
||||||
OffThreadCompileScriptCallback(void* token, void* callbackData)
|
OffThreadCompileScriptCallback(void* token, void* callbackData)
|
||||||
{
|
{
|
||||||
ShellContext* sc = static_cast<ShellContext*>(callbackData);
|
auto job = static_cast<OffThreadJob*>(callbackData);
|
||||||
sc->offThreadState.markDone(token);
|
job->markDone(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -4484,17 +4610,16 @@ OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
// Make sure we own the string's chars, so that they are not freed before
|
// Make sure we own the string's chars, so that they are not freed before
|
||||||
// the compilation is finished.
|
// the compilation is finished.
|
||||||
ScopedJSFreePtr<char16_t> ownedChars;
|
JS::UniqueTwoByteChars ownedChars;
|
||||||
if (stableChars.maybeGiveOwnershipToCaller()) {
|
if (stableChars.maybeGiveOwnershipToCaller()) {
|
||||||
ownedChars = const_cast<char16_t*>(chars);
|
ownedChars.reset(const_cast<char16_t*>(chars));
|
||||||
} else {
|
} else {
|
||||||
char16_t* copy = cx->pod_malloc<char16_t>(length);
|
ownedChars.reset(cx->pod_malloc<char16_t>(length));
|
||||||
if (!copy)
|
if (!ownedChars)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mozilla::PodCopy(copy, chars, length);
|
mozilla::PodCopy(ownedChars.get(), chars, length);
|
||||||
ownedChars = copy;
|
chars = ownedChars.get();
|
||||||
chars = copy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JS::CanCompileOffThread(cx, options, length)) {
|
if (!JS::CanCompileOffThread(cx, options, length)) {
|
||||||
|
|
@ -4503,20 +4628,18 @@ OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellContext* sc = GetShellContext(cx);
|
ShellContext* sc = GetShellContext(cx);
|
||||||
if (!sc->offThreadState.startIfIdle(cx, ScriptKind::Script, ownedChars)) {
|
OffThreadJob* job = sc->offThreadState.newJob(cx, ScriptKind::Script, Move(ownedChars));
|
||||||
JS_ReportErrorASCII(cx, "called offThreadCompileScript without calling runOffThreadScript"
|
if (!job)
|
||||||
" to receive prior off-thread compilation");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!JS::CompileOffThread(cx, options, chars, length,
|
if (!JS::CompileOffThread(cx, options, chars, length,
|
||||||
OffThreadCompileScriptCallback, sc))
|
OffThreadCompileScriptCallback, job))
|
||||||
{
|
{
|
||||||
sc->offThreadState.abandon(cx);
|
sc->offThreadState.deleteJob(cx, job);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setInt32(job->id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4529,11 +4652,14 @@ runOffThreadScript(JSContext* cx, unsigned argc, Value* vp)
|
||||||
gc::FinishGC(cx);
|
gc::FinishGC(cx);
|
||||||
|
|
||||||
ShellContext* sc = GetShellContext(cx);
|
ShellContext* sc = GetShellContext(cx);
|
||||||
void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::Script);
|
OffThreadJob* job = sc->offThreadState.lookupJobForArgs(cx, ScriptKind::Script, args, 0);
|
||||||
if (!token) {
|
if (!job)
|
||||||
JS_ReportErrorASCII(cx, "called runOffThreadScript when no compilation is pending");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
void* token = job->waitUntilDone(cx);
|
||||||
|
MOZ_ASSERT(token);
|
||||||
|
|
||||||
|
sc->offThreadState.deleteJob(cx, job);
|
||||||
|
|
||||||
RootedScript script(cx, JS::FinishOffThreadScript(cx, token));
|
RootedScript script(cx, JS::FinishOffThreadScript(cx, token));
|
||||||
if (!script)
|
if (!script)
|
||||||
|
|
@ -4571,17 +4697,16 @@ OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
// Make sure we own the string's chars, so that they are not freed before
|
// Make sure we own the string's chars, so that they are not freed before
|
||||||
// the compilation is finished.
|
// the compilation is finished.
|
||||||
ScopedJSFreePtr<char16_t> ownedChars;
|
JS::UniqueTwoByteChars ownedChars;
|
||||||
if (stableChars.maybeGiveOwnershipToCaller()) {
|
if (stableChars.maybeGiveOwnershipToCaller()) {
|
||||||
ownedChars = const_cast<char16_t*>(chars);
|
ownedChars.reset(const_cast<char16_t*>(chars));
|
||||||
} else {
|
} else {
|
||||||
char16_t* copy = cx->pod_malloc<char16_t>(length);
|
ownedChars.reset(cx->pod_malloc<char16_t>(length));
|
||||||
if (!copy)
|
if (!ownedChars)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mozilla::PodCopy(copy, chars, length);
|
mozilla::PodCopy(ownedChars.get(), chars, length);
|
||||||
ownedChars = copy;
|
chars = ownedChars.get();
|
||||||
chars = copy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JS::CanCompileOffThread(cx, options, length)) {
|
if (!JS::CanCompileOffThread(cx, options, length)) {
|
||||||
|
|
@ -4590,20 +4715,18 @@ OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellContext* sc = GetShellContext(cx);
|
ShellContext* sc = GetShellContext(cx);
|
||||||
if (!sc->offThreadState.startIfIdle(cx, ScriptKind::Module, ownedChars)) {
|
OffThreadJob* job = sc->offThreadState.newJob(cx, ScriptKind::Module, Move(ownedChars));
|
||||||
JS_ReportErrorASCII(cx, "called offThreadCompileModule without receiving prior off-thread "
|
if (!job)
|
||||||
"compilation");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!JS::CompileOffThreadModule(cx, options, chars, length,
|
if (!JS::CompileOffThreadModule(cx, options, chars, length,
|
||||||
OffThreadCompileScriptCallback, sc))
|
OffThreadCompileScriptCallback, job))
|
||||||
{
|
{
|
||||||
sc->offThreadState.abandon(cx);
|
sc->offThreadState.deleteJob(cx, job);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setInt32(job->id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4616,11 +4739,14 @@ FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
gc::FinishGC(cx);
|
gc::FinishGC(cx);
|
||||||
|
|
||||||
ShellContext* sc = GetShellContext(cx);
|
ShellContext* sc = GetShellContext(cx);
|
||||||
void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::Module);
|
OffThreadJob* job = sc->offThreadState.lookupJobForArgs(cx, ScriptKind::Module, args, 0);
|
||||||
if (!token) {
|
if (!job)
|
||||||
JS_ReportErrorASCII(cx, "called finishOffThreadModule when no compilation is pending");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
void* token = job->waitUntilDone(cx);
|
||||||
|
MOZ_ASSERT(token);
|
||||||
|
|
||||||
|
sc->offThreadState.deleteJob(cx, job);
|
||||||
|
|
||||||
RootedObject module(cx, JS::FinishOffThreadModule(cx, token));
|
RootedObject module(cx, JS::FinishOffThreadModule(cx, token));
|
||||||
if (!module)
|
if (!module)
|
||||||
|
|
@ -6237,7 +6363,8 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||||
JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
|
JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
|
||||||
"offThreadCompileScript(code[, options])",
|
"offThreadCompileScript(code[, options])",
|
||||||
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
|
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
|
||||||
" and run the code, call |runOffThreadScript|. If present, |options| may\n"
|
" and run the code, call |runOffThreadScript|. The function returns a job ID.\n"
|
||||||
|
" If present, |options| may\n"
|
||||||
" have properties saying how the code should be compiled:\n"
|
" have properties saying how the code should be compiled:\n"
|
||||||
" noScriptRval: use the no-script-rval compiler option (default: false)\n"
|
" noScriptRval: use the no-script-rval compiler option (default: false)\n"
|
||||||
" fileName: filename for error messages and debug info\n"
|
" fileName: filename for error messages and debug info\n"
|
||||||
|
|
@ -6252,19 +6379,22 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||||
" Debugger.Source.prototype.elementAttributeName returns.\n"),
|
" Debugger.Source.prototype.elementAttributeName returns.\n"),
|
||||||
|
|
||||||
JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
|
JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
|
||||||
"runOffThreadScript()",
|
"runOffThreadScript([jobID])",
|
||||||
" Wait for off-thread compilation to complete. If an error occurred,\n"
|
" Wait for an off-thread compilation job to complete. The job ID can be\n"
|
||||||
|
" omitted if there is only one job pending. If an error occurred,\n"
|
||||||
" throw the appropriate exception; otherwise, run the script and return\n"
|
" throw the appropriate exception; otherwise, run the script and return\n"
|
||||||
" its value."),
|
" its value."),
|
||||||
|
|
||||||
JS_FN_HELP("offThreadCompileModule", OffThreadCompileModule, 1, 0,
|
JS_FN_HELP("offThreadCompileModule", OffThreadCompileModule, 1, 0,
|
||||||
"offThreadCompileModule(code)",
|
"offThreadCompileModule(code)",
|
||||||
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
|
" Compile |code| on a helper thread, returning a job ID. To wait for the\n"
|
||||||
" and get the module object, call |finishOffThreadModule|."),
|
" compilation to finish and get the module object, call |finishOffThreadModule|\n"
|
||||||
|
" passing the job ID."),
|
||||||
|
|
||||||
JS_FN_HELP("finishOffThreadModule", FinishOffThreadModule, 0, 0,
|
JS_FN_HELP("finishOffThreadModule", FinishOffThreadModule, 0, 0,
|
||||||
"finishOffThreadModule()",
|
"finishOffThreadModule([jobID])",
|
||||||
" Wait for off-thread compilation to complete. If an error occurred,\n"
|
" Wait for an off-thread compilation job to complete. The job ID can be\n"
|
||||||
|
" omitted if there is only one job pending. If an error occurred,\n"
|
||||||
" throw the appropriate exception; otherwise, return the module object"),
|
" throw the appropriate exception; otherwise, return the module object"),
|
||||||
|
|
||||||
JS_FN_HELP("timeout", Timeout, 1, 0,
|
JS_FN_HELP("timeout", Timeout, 1, 0,
|
||||||
|
|
@ -8308,6 +8438,8 @@ main(int argc, char** argv, char** envp)
|
||||||
|
|
||||||
DestructSharedArrayBufferMailbox();
|
DestructSharedArrayBufferMailbox();
|
||||||
|
|
||||||
|
sc->offThreadState.cancelAll(cx);
|
||||||
|
|
||||||
JS_DestroyContext(cx);
|
JS_DestroyContext(cx);
|
||||||
JS_ShutDown();
|
JS_ShutDown();
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -860,19 +860,31 @@ GlobalHelperThreadState::waitForAllThreads()
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool
|
bool
|
||||||
GlobalHelperThreadState::checkTaskThreadLimit(size_t maxThreads) const
|
GlobalHelperThreadState::checkTaskThreadLimit(size_t maxThreads, bool isMaster) const
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(maxThreads > 0);
|
||||||
|
|
||||||
if (maxThreads >= threadCount)
|
if (maxThreads >= threadCount)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
size_t idle = 0;
|
||||||
for (auto& thread : *threads) {
|
for (auto& thread : *threads) {
|
||||||
if (thread.currentTask.isSome() && thread.currentTask->is<T>())
|
if (thread.currentTask.isSome()) {
|
||||||
count++;
|
if (thread.currentTask->is<T>())
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
idle++;
|
||||||
|
}
|
||||||
if (count >= maxThreads)
|
if (count >= maxThreads)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(idle > 0);
|
||||||
|
|
||||||
|
if (isMaster && idle <= 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1091,7 +1103,11 @@ GlobalHelperThreadState::pendingIonCompileHasSufficientPriority(
|
||||||
bool
|
bool
|
||||||
GlobalHelperThreadState::canStartParseTask(const AutoLockHelperThreadState& lock)
|
GlobalHelperThreadState::canStartParseTask(const AutoLockHelperThreadState& lock)
|
||||||
{
|
{
|
||||||
return !parseWorklist(lock).empty() && checkTaskThreadLimit<ParseTask*>(maxParseThreads());
|
// Parse tasks that end up compiling asm.js may use Wasm compilation threads
|
||||||
|
// to generate machine code. We do not know ahead of time which parse tasks
|
||||||
|
// will need those resources, so conservatively treat them as master tasks.
|
||||||
|
return !parseWorklist(lock).empty() &&
|
||||||
|
checkTaskThreadLimit<ParseTask*>(maxParseThreads(), /*isMaster=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -1892,49 +1908,56 @@ HelperThread::threadLoop()
|
||||||
while (true) {
|
while (true) {
|
||||||
MOZ_ASSERT(idle());
|
MOZ_ASSERT(idle());
|
||||||
|
|
||||||
// Block until a task is available. Save the value of whether we are
|
js::oom::ThreadType task = js::oom::THREAD_TYPE_NONE;
|
||||||
// going to do an Ion compile, in case the value returned by the method
|
|
||||||
// changes.
|
|
||||||
bool ionCompile = false;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (terminate)
|
if (terminate)
|
||||||
return;
|
return;
|
||||||
if ((ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority(lock)) ||
|
if (HelperThreadState().canStartGCParallelTask(lock))
|
||||||
HelperThreadState().canStartWasmCompile(lock) ||
|
task = js::oom::THREAD_TYPE_GCPARALLEL;
|
||||||
HelperThreadState().canStartPromiseTask(lock) ||
|
else if (HelperThreadState().canStartGCHelperTask(lock))
|
||||||
HelperThreadState().canStartParseTask(lock) ||
|
task = js::oom::THREAD_TYPE_GCHELPER;
|
||||||
HelperThreadState().canStartCompressionTask(lock) ||
|
else if (HelperThreadState().pendingIonCompileHasSufficientPriority(lock))
|
||||||
HelperThreadState().canStartGCHelperTask(lock) ||
|
task = js::oom::THREAD_TYPE_ION;
|
||||||
HelperThreadState().canStartGCParallelTask(lock))
|
else if (HelperThreadState().canStartWasmCompile(lock))
|
||||||
{
|
task = js::oom::THREAD_TYPE_ASMJS;
|
||||||
|
else if (HelperThreadState().canStartPromiseTask(lock))
|
||||||
|
task = js::oom::THREAD_TYPE_PROMISE_TASK;
|
||||||
|
else if (HelperThreadState().canStartParseTask(lock))
|
||||||
|
task = js::oom::THREAD_TYPE_PARSE;
|
||||||
|
else if (HelperThreadState().canStartCompressionTask(lock))
|
||||||
|
task = js::oom::THREAD_TYPE_COMPRESS;
|
||||||
|
|
||||||
|
if (task != js::oom::THREAD_TYPE_NONE)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
|
HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ionCompile) {
|
js::oom::SetThreadType(task);
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
|
switch (task) {
|
||||||
handleIonWorkload(lock);
|
case js::oom::THREAD_TYPE_GCPARALLEL:
|
||||||
} else if (HelperThreadState().canStartWasmCompile(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_ASMJS);
|
|
||||||
handleWasmWorkload(lock);
|
|
||||||
} else if (HelperThreadState().canStartPromiseTask(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_PROMISE_TASK);
|
|
||||||
handlePromiseTaskWorkload(lock);
|
|
||||||
} else if (HelperThreadState().canStartParseTask(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_PARSE);
|
|
||||||
handleParseWorkload(lock, stackLimit);
|
|
||||||
} else if (HelperThreadState().canStartCompressionTask(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_COMPRESS);
|
|
||||||
handleCompressionWorkload(lock);
|
|
||||||
} else if (HelperThreadState().canStartGCHelperTask(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_GCHELPER);
|
|
||||||
handleGCHelperWorkload(lock);
|
|
||||||
} else if (HelperThreadState().canStartGCParallelTask(lock)) {
|
|
||||||
js::oom::SetThreadType(js::oom::THREAD_TYPE_GCPARALLEL);
|
|
||||||
handleGCParallelWorkload(lock);
|
handleGCParallelWorkload(lock);
|
||||||
} else {
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_GCHELPER:
|
||||||
|
handleGCHelperWorkload(lock);
|
||||||
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_ION:
|
||||||
|
handleIonWorkload(lock);
|
||||||
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_ASMJS:
|
||||||
|
handleWasmWorkload(lock);
|
||||||
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_PROMISE_TASK:
|
||||||
|
handlePromiseTaskWorkload(lock);
|
||||||
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_PARSE:
|
||||||
|
handleParseWorkload(lock, stackLimit);
|
||||||
|
break;
|
||||||
|
case js::oom::THREAD_TYPE_COMPRESS:
|
||||||
|
handleCompressionWorkload(lock);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
MOZ_CRASH("No task to perform");
|
MOZ_CRASH("No task to perform");
|
||||||
}
|
}
|
||||||
|
js::oom::SetThreadType(js::oom::THREAD_TYPE_NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ class GlobalHelperThreadState
|
||||||
void waitForAllThreads();
|
void waitForAllThreads();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool checkTaskThreadLimit(size_t maxThreads) const;
|
bool checkTaskThreadLimit(size_t maxThreads, bool isMaster = false) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue