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 : bd4e6b296e7a02dbe2370a515174dcd1a6a57ffa
This commit is contained in:
Luke Wagner 2014-01-20 18:00:18 -06:00
parent 0372a045dc
commit d5fde93cb3
7 changed files with 43 additions and 18 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

@ -4442,8 +4442,22 @@ 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;
// 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 +4466,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

@ -3647,7 +3647,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,16 @@ 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;
}
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");