mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 961318 - Tweak off-main-thread parsing heuristic to avoid delaying execution when an atoms-zone GC is in progress (r=billm)
--HG-- extra : rebase_source : aa831d41e1b04062e208d5c36697da1f5e1c7d07
This commit is contained in:
parent
e3dfa27d04
commit
2108045c2d
@ -841,7 +841,7 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
|
||||
JS::CompileOptions options(cx);
|
||||
FillCompileOptionsForRequest(aRequest, global, &options);
|
||||
|
||||
if (!JS::CanCompileOffThread(cx, options)) {
|
||||
if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.Length())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -2656,7 +2656,7 @@ nsXULPrototypeScript::Compile(const char16_t* aText,
|
||||
JS::ExposeObjectToActiveJS(scope);
|
||||
}
|
||||
|
||||
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options)) {
|
||||
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
|
||||
if (!JS::CompileOffThread(cx, scope, options,
|
||||
static_cast<const jschar*>(aText), aTextLength,
|
||||
OffThreadScriptReceiverCallback,
|
||||
|
@ -17,13 +17,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=929236
|
||||
|
||||
<script>
|
||||
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
|
||||
ok(jsFuns.isAsmJSCompilationAvailable());
|
||||
ok(jsFuns.isAsmJSCompilationAvailable(), "asm.js compilation is available");
|
||||
|
||||
// generate a big ol asm.js module and compile it async so that we can hit
|
||||
// the asm.js cache.
|
||||
|
||||
var code = "function f() { 'use asm';\n";
|
||||
for (var i = 0; i < 1000; i++)
|
||||
for (var i = 0; i < 5000; i++)
|
||||
code += "function g" + i + "() { return " + i + "}\n";
|
||||
code += "return g42 }\n";
|
||||
code += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
|
||||
@ -31,7 +31,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=929236
|
||||
code += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function')\n";
|
||||
code += "ok(g42() === 42, 'g42 returns the correct result')\n";
|
||||
code += "finishedEvalAsync(f);";
|
||||
ok(code.length > 10000);
|
||||
ok(code.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
function evalAsync(code) {
|
||||
var blob = new Blob([code], {type:"application/javascript"});
|
||||
|
@ -23,9 +23,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
|
||||
// so that we can hit the asm.js cache.
|
||||
|
||||
var code = "function f() { 'use asm';\n";
|
||||
for (var i = 0; i < 1000; i++)
|
||||
for (var i = 0; i < 5000; i++)
|
||||
code += "function g" + i + "() { return " + i + "}\n";
|
||||
ok(code.length > 10000, "code is long enough");
|
||||
ok(code.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
const N = 4;
|
||||
|
||||
|
@ -20,10 +20,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=941830
|
||||
ok(jsFuns.isAsmJSCompilationAvailable());
|
||||
|
||||
var asmjsCode = "function f() { 'use asm';";
|
||||
for (var i = 0; i < 1000; i++)
|
||||
for (var i = 0; i < 5000; i++)
|
||||
asmjsCode += "function g" + i + "() { return " + i + "}";
|
||||
asmjsCode += "return g42 }";
|
||||
ok(asmjsCode.length > 10000);
|
||||
ok(asmjsCode.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
var workerCode = asmjsCode;
|
||||
workerCode += "if (f()() !== 42) postMessage('fail'); else postMessage('ok');";
|
||||
|
@ -4442,8 +4442,26 @@ JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optio
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options)
|
||||
JS::CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t length)
|
||||
{
|
||||
static const unsigned TINY_LENGTH = 1000;
|
||||
static const unsigned HUGE_LENGTH = 100*1000;
|
||||
|
||||
// These are heuristics which the caller may choose to ignore (e.g., for
|
||||
// testing purposes).
|
||||
if (!options.forceAsync) {
|
||||
// Compiling off the main thread inolves creating a new Zone and other
|
||||
// significant overheads. Don't bother if the script is tiny.
|
||||
if (length < TINY_LENGTH)
|
||||
return false;
|
||||
|
||||
// If the parsing task would have to wait for GC to complete, it'll probably
|
||||
// be faster to just start it synchronously on the main thread unless the
|
||||
// script is huge.
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime()) && length < HUGE_LENGTH)
|
||||
return false;
|
||||
}
|
||||
|
||||
return cx->runtime()->canUseParallelParsing();
|
||||
}
|
||||
|
||||
@ -4452,7 +4470,7 @@ JS::CompileOffThread(JSContext *cx, Handle<JSObject*> obj, const ReadOnlyCompile
|
||||
const jschar *chars, size_t length,
|
||||
OffThreadCompileCallback callback, void *callbackData)
|
||||
{
|
||||
JS_ASSERT(CanCompileOffThread(cx, options));
|
||||
JS_ASSERT(CanCompileOffThread(cx, options, length));
|
||||
return StartOffThreadParseScript(cx, options, chars, length, obj, callback, callbackData);
|
||||
}
|
||||
|
||||
|
@ -3466,6 +3466,7 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
|
||||
extraWarningsOption(false),
|
||||
werrorOption(false),
|
||||
asmJSOption(false),
|
||||
forceAsync(false),
|
||||
sourcePolicy(SAVE_SOURCE)
|
||||
{ }
|
||||
|
||||
@ -3498,6 +3499,7 @@ class JS_FRIEND_API(ReadOnlyCompileOptions)
|
||||
bool extraWarningsOption;
|
||||
bool werrorOption;
|
||||
bool asmJSOption;
|
||||
bool forceAsync;
|
||||
enum SourcePolicy {
|
||||
NO_SOURCE,
|
||||
LAZY_SOURCE,
|
||||
@ -3647,7 +3649,7 @@ extern JS_PUBLIC_API(JSScript *)
|
||||
Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options, const char *filename);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options);
|
||||
CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t length);
|
||||
|
||||
/*
|
||||
* Off thread compilation control flow.
|
||||
|
@ -229,6 +229,18 @@ ParseTask::~ParseTask()
|
||||
js_delete(errors[i]);
|
||||
}
|
||||
|
||||
bool
|
||||
js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
|
||||
{
|
||||
// Off thread parsing can't occur during incremental collections on the
|
||||
// atoms compartment, to avoid triggering barriers. (Outside the atoms
|
||||
// compartment, the compilation will use a new zone that is never
|
||||
// collected.) If an atoms-zone GC is in progress, hold off on executing the
|
||||
// parse task until the atoms-zone GC completes (see
|
||||
// EnqueuePendingParseTasksAfterGC).
|
||||
return rt->activeGCInAtomsZone();
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
||||
const jschar *chars, size_t length, HandleObject scopeChain,
|
||||
@ -299,13 +311,7 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
||||
WorkerThreadState &state = *cx->runtime()->workerThreadState;
|
||||
JS_ASSERT(state.numThreads);
|
||||
|
||||
// Off thread parsing can't occur during incremental collections on the
|
||||
// atoms compartment, to avoid triggering barriers. (Outside the atoms
|
||||
// compartment, the compilation will use a new zone which doesn't require
|
||||
// barriers itself.) If an atoms-zone GC is in progress, hold off on
|
||||
// executing the parse task until the atoms-zone GC completes (see
|
||||
// EnqueuePendingParseTasksAfterGC).
|
||||
if (cx->runtime()->activeGCInAtomsZone()) {
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||
if (!state.parseWaitingOnGC.append(task.get()))
|
||||
return false;
|
||||
} else {
|
||||
@ -327,7 +333,7 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
||||
void
|
||||
js::EnqueuePendingParseTasksAfterGC(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(!rt->activeGCInAtomsZone());
|
||||
JS_ASSERT(!OffThreadParsingMustWaitForGC(rt));
|
||||
|
||||
if (!rt->workerThreadState || rt->workerThreadState->parseWaitingOnGC.empty())
|
||||
return;
|
||||
|
@ -371,6 +371,11 @@ struct ParseTask
|
||||
~ParseTask();
|
||||
};
|
||||
|
||||
// Return whether, if a new parse task was started, it would need to wait for
|
||||
// an in-progress GC to complete before starting.
|
||||
extern bool
|
||||
OffThreadParsingMustWaitForGC(JSRuntime *rt);
|
||||
|
||||
// Compression tasks are allocated on the stack by their triggering thread,
|
||||
// which will block on the compression completing as the task goes out of scope
|
||||
// to ensure it completes at the required time.
|
||||
|
@ -3304,16 +3304,20 @@ OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
|
||||
.setCompileAndGo(true)
|
||||
.setSourcePolicy(CompileOptions::SAVE_SOURCE);
|
||||
|
||||
if (!JS::CanCompileOffThread(cx, options)) {
|
||||
JS_ReportError(cx, "cannot compile code on worker thread");
|
||||
return false;
|
||||
}
|
||||
// We assume the caller wants caching if at all possible, ignoring
|
||||
// heuristics that make sense for a real browser.
|
||||
options.forceAsync = true;
|
||||
|
||||
const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
|
||||
if (!chars)
|
||||
return false;
|
||||
size_t length = JS_GetStringLength(scriptContents);
|
||||
|
||||
if (!JS::CanCompileOffThread(cx, options, length)) {
|
||||
JS_ReportError(cx, "cannot compile code on worker thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!offThreadState.startIfIdle(cx, scriptContents)) {
|
||||
JS_ReportError(cx, "called offThreadCompileScript without calling runOffThreadScript"
|
||||
" to receive prior off-thread compilation");
|
||||
|
Loading…
Reference in New Issue
Block a user