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:
Luke Wagner 2014-01-20 18:00:18 -06:00
parent e3dfa27d04
commit 2108045c2d
10 changed files with 59 additions and 24 deletions

View File

@ -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;
}

View File

@ -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,

View File

@ -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"});

View File

@ -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;

View File

@ -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');";

View File

@ -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);
}

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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");