js/src/jsworkers.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jsworkers.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1133 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jsworkers.h"
    1.11 +
    1.12 +#ifdef JS_THREADSAFE
    1.13 +
    1.14 +#include "mozilla/DebugOnly.h"
    1.15 +
    1.16 +#include "jsnativestack.h"
    1.17 +#include "prmjtime.h"
    1.18 +
    1.19 +#include "frontend/BytecodeCompiler.h"
    1.20 +#include "jit/IonBuilder.h"
    1.21 +#include "vm/Debugger.h"
    1.22 +#include "vm/TraceLogging.h"
    1.23 +
    1.24 +#include "jscntxtinlines.h"
    1.25 +#include "jscompartmentinlines.h"
    1.26 +#include "jsobjinlines.h"
    1.27 +#include "jsscriptinlines.h"
    1.28 +
    1.29 +using namespace js;
    1.30 +
    1.31 +using mozilla::ArrayLength;
    1.32 +using mozilla::DebugOnly;
    1.33 +
    1.34 +namespace js {
    1.35 +
    1.36 +GlobalWorkerThreadState gWorkerThreadState;
    1.37 +
    1.38 +} // namespace js
    1.39 +
    1.40 +void
    1.41 +js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
    1.42 +{
    1.43 +    // If 'cx' is not a JSContext, we are already off the main thread and the
    1.44 +    // worker threads would have already been initialized.
    1.45 +    if (!cx->isJSContext())
    1.46 +        return;
    1.47 +
    1.48 +    WorkerThreadState().ensureInitialized();
    1.49 +}
    1.50 +
    1.51 +static size_t
    1.52 +ThreadCountForCPUCount(size_t cpuCount)
    1.53 +{
    1.54 +    return Max(cpuCount, (size_t)2);
    1.55 +}
    1.56 +
    1.57 +void
    1.58 +js::SetFakeCPUCount(size_t count)
    1.59 +{
    1.60 +    // This must be called before the threads have been initialized.
    1.61 +    JS_ASSERT(!WorkerThreadState().threads);
    1.62 +
    1.63 +    WorkerThreadState().cpuCount = count;
    1.64 +    WorkerThreadState().threadCount = ThreadCountForCPUCount(count);
    1.65 +}
    1.66 +
    1.67 +#ifdef JS_ION
    1.68 +
    1.69 +bool
    1.70 +js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
    1.71 +{
    1.72 +    // Threads already initialized by the AsmJS compiler.
    1.73 +    JS_ASSERT(asmData->mir);
    1.74 +    JS_ASSERT(asmData->lir == nullptr);
    1.75 +
    1.76 +    AutoLockWorkerThreadState lock;
    1.77 +
    1.78 +    // Don't append this task if another failed.
    1.79 +    if (WorkerThreadState().asmJSWorkerFailed())
    1.80 +        return false;
    1.81 +
    1.82 +    if (!WorkerThreadState().asmJSWorklist().append(asmData))
    1.83 +        return false;
    1.84 +
    1.85 +    WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
    1.86 +    return true;
    1.87 +}
    1.88 +
    1.89 +bool
    1.90 +js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
    1.91 +{
    1.92 +    EnsureWorkerThreadsInitialized(cx);
    1.93 +
    1.94 +    AutoLockWorkerThreadState lock;
    1.95 +
    1.96 +    if (!WorkerThreadState().ionWorklist().append(builder))
    1.97 +        return false;
    1.98 +
    1.99 +    WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
   1.100 +    return true;
   1.101 +}
   1.102 +
   1.103 +/*
   1.104 + * Move an IonBuilder for which compilation has either finished, failed, or
   1.105 + * been cancelled into the global finished compilation list. All off thread
   1.106 + * compilations which are started must eventually be finished.
   1.107 + */
   1.108 +static void
   1.109 +FinishOffThreadIonCompile(jit::IonBuilder *builder)
   1.110 +{
   1.111 +    WorkerThreadState().ionFinishedList().append(builder);
   1.112 +}
   1.113 +
   1.114 +#endif // JS_ION
   1.115 +
   1.116 +static inline bool
   1.117 +CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
   1.118 +{
   1.119 +    if (script)
   1.120 +        return target == script;
   1.121 +    return target->compartment() == compartment;
   1.122 +}
   1.123 +
   1.124 +void
   1.125 +js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
   1.126 +{
   1.127 +#ifdef JS_ION
   1.128 +    jit::JitCompartment *jitComp = compartment->jitCompartment();
   1.129 +    if (!jitComp)
   1.130 +        return;
   1.131 +
   1.132 +    AutoLockWorkerThreadState lock;
   1.133 +
   1.134 +    if (!WorkerThreadState().threads)
   1.135 +        return;
   1.136 +
   1.137 +    /* Cancel any pending entries for which processing hasn't started. */
   1.138 +    GlobalWorkerThreadState::IonBuilderVector &worklist = WorkerThreadState().ionWorklist();
   1.139 +    for (size_t i = 0; i < worklist.length(); i++) {
   1.140 +        jit::IonBuilder *builder = worklist[i];
   1.141 +        if (CompiledScriptMatches(compartment, script, builder->script())) {
   1.142 +            FinishOffThreadIonCompile(builder);
   1.143 +            WorkerThreadState().remove(worklist, &i);
   1.144 +        }
   1.145 +    }
   1.146 +
   1.147 +    /* Wait for in progress entries to finish up. */
   1.148 +    for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
   1.149 +        const WorkerThread &helper = WorkerThreadState().threads[i];
   1.150 +        while (helper.ionBuilder &&
   1.151 +               CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
   1.152 +        {
   1.153 +            helper.ionBuilder->cancel();
   1.154 +            WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
   1.155 +        }
   1.156 +    }
   1.157 +
   1.158 +    /* Cancel code generation for any completed entries. */
   1.159 +    GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
   1.160 +    for (size_t i = 0; i < finished.length(); i++) {
   1.161 +        jit::IonBuilder *builder = finished[i];
   1.162 +        if (CompiledScriptMatches(compartment, script, builder->script())) {
   1.163 +            jit::FinishOffThreadBuilder(builder);
   1.164 +            WorkerThreadState().remove(finished, &i);
   1.165 +        }
   1.166 +    }
   1.167 +#endif // JS_ION
   1.168 +}
   1.169 +
   1.170 +static const JSClass workerGlobalClass = {
   1.171 +    "internal-worker-global", JSCLASS_GLOBAL_FLAGS,
   1.172 +    JS_PropertyStub,  JS_DeletePropertyStub,
   1.173 +    JS_PropertyStub,  JS_StrictPropertyStub,
   1.174 +    JS_EnumerateStub, JS_ResolveStub,
   1.175 +    JS_ConvertStub,   nullptr,
   1.176 +    nullptr, nullptr, nullptr,
   1.177 +    JS_GlobalObjectTraceHook
   1.178 +};
   1.179 +
   1.180 +ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
   1.181 +                     const jschar *chars, size_t length,
   1.182 +                     JS::OffThreadCompileCallback callback, void *callbackData)
   1.183 +  : cx(cx), options(initCx), chars(chars), length(length),
   1.184 +    alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
   1.185 +    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
   1.186 +    optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
   1.187 +    script(nullptr), errors(cx), overRecursed(false)
   1.188 +{
   1.189 +}
   1.190 +
   1.191 +bool
   1.192 +ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
   1.193 +{
   1.194 +    if (!this->options.copy(cx, options))
   1.195 +        return false;
   1.196 +
   1.197 +    // Save those compilation options that the ScriptSourceObject can't
   1.198 +    // point at while it's in the compilation's temporary compartment.
   1.199 +    optionsElement = this->options.element();
   1.200 +    this->options.setElement(nullptr);
   1.201 +    optionsIntroductionScript = this->options.introductionScript();
   1.202 +    this->options.setIntroductionScript(nullptr);
   1.203 +
   1.204 +    return true;
   1.205 +}
   1.206 +
   1.207 +void
   1.208 +ParseTask::activate(JSRuntime *rt)
   1.209 +{
   1.210 +    rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
   1.211 +    cx->enterCompartment(exclusiveContextGlobal->compartment());
   1.212 +}
   1.213 +
   1.214 +void
   1.215 +ParseTask::finish()
   1.216 +{
   1.217 +    if (script) {
   1.218 +        // Initialize the ScriptSourceObject slots that we couldn't while the SSO
   1.219 +        // was in the temporary compartment.
   1.220 +        ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
   1.221 +        sso.initElement(optionsElement);
   1.222 +        sso.initIntroductionScript(optionsIntroductionScript);
   1.223 +    }
   1.224 +}
   1.225 +
   1.226 +ParseTask::~ParseTask()
   1.227 +{
   1.228 +    // ParseTask takes over ownership of its input exclusive context.
   1.229 +    js_delete(cx);
   1.230 +
   1.231 +    for (size_t i = 0; i < errors.length(); i++)
   1.232 +        js_delete(errors[i]);
   1.233 +}
   1.234 +
   1.235 +void
   1.236 +js::CancelOffThreadParses(JSRuntime *rt)
   1.237 +{
   1.238 +    AutoLockWorkerThreadState lock;
   1.239 +
   1.240 +    if (!WorkerThreadState().threads)
   1.241 +        return;
   1.242 +
   1.243 +    // Instead of forcibly canceling pending parse tasks, just wait for all scheduled
   1.244 +    // and in progress ones to complete. Otherwise the final GC may not collect
   1.245 +    // everything due to zones being used off thread.
   1.246 +    while (true) {
   1.247 +        bool pending = false;
   1.248 +        GlobalWorkerThreadState::ParseTaskVector &worklist = WorkerThreadState().parseWorklist();
   1.249 +        for (size_t i = 0; i < worklist.length(); i++) {
   1.250 +            ParseTask *task = worklist[i];
   1.251 +            if (task->runtimeMatches(rt))
   1.252 +                pending = true;
   1.253 +        }
   1.254 +        if (!pending) {
   1.255 +            bool inProgress = false;
   1.256 +            for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
   1.257 +                ParseTask *task = WorkerThreadState().threads[i].parseTask;
   1.258 +                if (task && task->runtimeMatches(rt))
   1.259 +                    inProgress = true;
   1.260 +            }
   1.261 +            if (!inProgress)
   1.262 +                break;
   1.263 +        }
   1.264 +        WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
   1.265 +    }
   1.266 +
   1.267 +    // Clean up any parse tasks which haven't been finished by the main thread.
   1.268 +    GlobalWorkerThreadState::ParseTaskVector &finished = WorkerThreadState().parseFinishedList();
   1.269 +    while (true) {
   1.270 +        bool found = false;
   1.271 +        for (size_t i = 0; i < finished.length(); i++) {
   1.272 +            ParseTask *task = finished[i];
   1.273 +            if (task->runtimeMatches(rt)) {
   1.274 +                found = true;
   1.275 +                AutoUnlockWorkerThreadState unlock;
   1.276 +                WorkerThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task);
   1.277 +            }
   1.278 +        }
   1.279 +        if (!found)
   1.280 +            break;
   1.281 +    }
   1.282 +}
   1.283 +
   1.284 +bool
   1.285 +js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
   1.286 +{
   1.287 +    // Off thread parsing can't occur during incremental collections on the
   1.288 +    // atoms compartment, to avoid triggering barriers. (Outside the atoms
   1.289 +    // compartment, the compilation will use a new zone that is never
   1.290 +    // collected.) If an atoms-zone GC is in progress, hold off on executing the
   1.291 +    // parse task until the atoms-zone GC completes (see
   1.292 +    // EnqueuePendingParseTasksAfterGC).
   1.293 +    return rt->activeGCInAtomsZone();
   1.294 +}
   1.295 +
   1.296 +bool
   1.297 +js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
   1.298 +                              const jschar *chars, size_t length,
   1.299 +                              JS::OffThreadCompileCallback callback, void *callbackData)
   1.300 +{
   1.301 +    // Suppress GC so that calls below do not trigger a new incremental GC
   1.302 +    // which could require barriers on the atoms compartment.
   1.303 +    gc::AutoSuppressGC suppress(cx);
   1.304 +
   1.305 +    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
   1.306 +    frontend::MaybeCallSourceHandler(cx, options, srcBuf);
   1.307 +
   1.308 +    EnsureWorkerThreadsInitialized(cx);
   1.309 +
   1.310 +    JS::CompartmentOptions compartmentOptions(cx->compartment()->options());
   1.311 +    compartmentOptions.setZone(JS::FreshZone);
   1.312 +    compartmentOptions.setInvisibleToDebugger(true);
   1.313 +    compartmentOptions.setMergeable(true);
   1.314 +
   1.315 +    // Don't falsely inherit the host's global trace hook.
   1.316 +    compartmentOptions.setTrace(nullptr);
   1.317 +
   1.318 +    JSObject *global = JS_NewGlobalObject(cx, &workerGlobalClass, nullptr,
   1.319 +                                          JS::FireOnNewGlobalHook, compartmentOptions);
   1.320 +    if (!global)
   1.321 +        return false;
   1.322 +
   1.323 +    JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals);
   1.324 +
   1.325 +    RootedObject obj(cx);
   1.326 +
   1.327 +    // Initialize all classes needed for parsing while we are still on the main
   1.328 +    // thread. Do this for both the target and the new global so that prototype
   1.329 +    // pointers can be changed infallibly after parsing finishes.
   1.330 +    if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
   1.331 +        !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
   1.332 +        !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
   1.333 +        !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
   1.334 +    {
   1.335 +        return false;
   1.336 +    }
   1.337 +    {
   1.338 +        AutoCompartment ac(cx, global);
   1.339 +        if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
   1.340 +            !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
   1.341 +            !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
   1.342 +            !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
   1.343 +        {
   1.344 +            return false;
   1.345 +        }
   1.346 +    }
   1.347 +
   1.348 +    ScopedJSDeletePtr<ExclusiveContext> workercx(
   1.349 +        cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) nullptr,
   1.350 +                                   ThreadSafeContext::Context_Exclusive));
   1.351 +    if (!workercx)
   1.352 +        return false;
   1.353 +
   1.354 +    ScopedJSDeletePtr<ParseTask> task(
   1.355 +        cx->new_<ParseTask>(workercx.get(), global, cx, chars, length,
   1.356 +                            callback, callbackData));
   1.357 +    if (!task)
   1.358 +        return false;
   1.359 +
   1.360 +    workercx.forget();
   1.361 +
   1.362 +    if (!task->init(cx, options))
   1.363 +        return false;
   1.364 +
   1.365 +    if (OffThreadParsingMustWaitForGC(cx->runtime())) {
   1.366 +        AutoLockWorkerThreadState lock;
   1.367 +        if (!WorkerThreadState().parseWaitingOnGC().append(task.get()))
   1.368 +            return false;
   1.369 +    } else {
   1.370 +        task->activate(cx->runtime());
   1.371 +
   1.372 +        AutoLockWorkerThreadState lock;
   1.373 +
   1.374 +        if (!WorkerThreadState().parseWorklist().append(task.get()))
   1.375 +            return false;
   1.376 +
   1.377 +        WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
   1.378 +    }
   1.379 +
   1.380 +    task.forget();
   1.381 +
   1.382 +    return true;
   1.383 +}
   1.384 +
   1.385 +void
   1.386 +js::EnqueuePendingParseTasksAfterGC(JSRuntime *rt)
   1.387 +{
   1.388 +    JS_ASSERT(!OffThreadParsingMustWaitForGC(rt));
   1.389 +
   1.390 +    GlobalWorkerThreadState::ParseTaskVector newTasks;
   1.391 +    {
   1.392 +        AutoLockWorkerThreadState lock;
   1.393 +        GlobalWorkerThreadState::ParseTaskVector &waiting = WorkerThreadState().parseWaitingOnGC();
   1.394 +
   1.395 +        for (size_t i = 0; i < waiting.length(); i++) {
   1.396 +            ParseTask *task = waiting[i];
   1.397 +            if (task->runtimeMatches(rt)) {
   1.398 +                newTasks.append(task);
   1.399 +                WorkerThreadState().remove(waiting, &i);
   1.400 +            }
   1.401 +        }
   1.402 +    }
   1.403 +
   1.404 +    if (newTasks.empty())
   1.405 +        return;
   1.406 +
   1.407 +    // This logic should mirror the contents of the !activeGCInAtomsZone()
   1.408 +    // branch in StartOffThreadParseScript:
   1.409 +
   1.410 +    for (size_t i = 0; i < newTasks.length(); i++)
   1.411 +        newTasks[i]->activate(rt);
   1.412 +
   1.413 +    AutoLockWorkerThreadState lock;
   1.414 +
   1.415 +    for (size_t i = 0; i < newTasks.length(); i++)
   1.416 +        WorkerThreadState().parseWorklist().append(newTasks[i]);
   1.417 +
   1.418 +    WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
   1.419 +}
   1.420 +
   1.421 +static const uint32_t WORKER_STACK_SIZE = 512 * 1024;
   1.422 +static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
   1.423 +
   1.424 +void
   1.425 +GlobalWorkerThreadState::ensureInitialized()
   1.426 +{
   1.427 +    JS_ASSERT(this == &WorkerThreadState());
   1.428 +    AutoLockWorkerThreadState lock;
   1.429 +
   1.430 +    if (threads)
   1.431 +        return;
   1.432 +
   1.433 +    threads = js_pod_calloc<WorkerThread>(threadCount);
   1.434 +    if (!threads)
   1.435 +        CrashAtUnhandlableOOM("GlobalWorkerThreadState::ensureInitialized");
   1.436 +
   1.437 +    for (size_t i = 0; i < threadCount; i++) {
   1.438 +        WorkerThread &helper = threads[i];
   1.439 +        helper.threadData.construct(static_cast<JSRuntime *>(nullptr));
   1.440 +        helper.thread = PR_CreateThread(PR_USER_THREAD,
   1.441 +                                        WorkerThread::ThreadMain, &helper,
   1.442 +                                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, WORKER_STACK_SIZE);
   1.443 +        if (!helper.thread || !helper.threadData.ref().init())
   1.444 +            CrashAtUnhandlableOOM("GlobalWorkerThreadState::ensureInitialized");
   1.445 +    }
   1.446 +
   1.447 +    resetAsmJSFailureState();
   1.448 +}
   1.449 +
   1.450 +GlobalWorkerThreadState::GlobalWorkerThreadState()
   1.451 +{
   1.452 +    mozilla::PodZero(this);
   1.453 +
   1.454 +    cpuCount = GetCPUCount();
   1.455 +    threadCount = ThreadCountForCPUCount(cpuCount);
   1.456 +
   1.457 +    MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
   1.458 +
   1.459 +    workerLock = PR_NewLock();
   1.460 +    consumerWakeup = PR_NewCondVar(workerLock);
   1.461 +    producerWakeup = PR_NewCondVar(workerLock);
   1.462 +}
   1.463 +
   1.464 +void
   1.465 +GlobalWorkerThreadState::finish()
   1.466 +{
   1.467 +    if (threads) {
   1.468 +        for (size_t i = 0; i < threadCount; i++)
   1.469 +            threads[i].destroy();
   1.470 +        js_free(threads);
   1.471 +    }
   1.472 +
   1.473 +    PR_DestroyCondVar(consumerWakeup);
   1.474 +    PR_DestroyCondVar(producerWakeup);
   1.475 +    PR_DestroyLock(workerLock);
   1.476 +}
   1.477 +
   1.478 +void
   1.479 +GlobalWorkerThreadState::lock()
   1.480 +{
   1.481 +    JS_ASSERT(!isLocked());
   1.482 +    AssertCurrentThreadCanLock(WorkerThreadStateLock);
   1.483 +    PR_Lock(workerLock);
   1.484 +#ifdef DEBUG
   1.485 +    lockOwner = PR_GetCurrentThread();
   1.486 +#endif
   1.487 +}
   1.488 +
   1.489 +void
   1.490 +GlobalWorkerThreadState::unlock()
   1.491 +{
   1.492 +    JS_ASSERT(isLocked());
   1.493 +#ifdef DEBUG
   1.494 +    lockOwner = nullptr;
   1.495 +#endif
   1.496 +    PR_Unlock(workerLock);
   1.497 +}
   1.498 +
   1.499 +#ifdef DEBUG
   1.500 +bool
   1.501 +GlobalWorkerThreadState::isLocked()
   1.502 +{
   1.503 +    return lockOwner == PR_GetCurrentThread();
   1.504 +}
   1.505 +#endif
   1.506 +
   1.507 +void
   1.508 +GlobalWorkerThreadState::wait(CondVar which, uint32_t millis)
   1.509 +{
   1.510 +    JS_ASSERT(isLocked());
   1.511 +#ifdef DEBUG
   1.512 +    lockOwner = nullptr;
   1.513 +#endif
   1.514 +    DebugOnly<PRStatus> status =
   1.515 +        PR_WaitCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup,
   1.516 +                       millis ? PR_MillisecondsToInterval(millis) : PR_INTERVAL_NO_TIMEOUT);
   1.517 +    JS_ASSERT(status == PR_SUCCESS);
   1.518 +#ifdef DEBUG
   1.519 +    lockOwner = PR_GetCurrentThread();
   1.520 +#endif
   1.521 +}
   1.522 +
   1.523 +void
   1.524 +GlobalWorkerThreadState::notifyAll(CondVar which)
   1.525 +{
   1.526 +    JS_ASSERT(isLocked());
   1.527 +    PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
   1.528 +}
   1.529 +
   1.530 +void
   1.531 +GlobalWorkerThreadState::notifyOne(CondVar which)
   1.532 +{
   1.533 +    JS_ASSERT(isLocked());
   1.534 +    PR_NotifyCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
   1.535 +}
   1.536 +
   1.537 +bool
   1.538 +GlobalWorkerThreadState::canStartAsmJSCompile()
   1.539 +{
   1.540 +    // Don't execute an AsmJS job if an earlier one failed.
   1.541 +    JS_ASSERT(isLocked());
   1.542 +    return !asmJSWorklist().empty() && !numAsmJSFailedJobs;
   1.543 +}
   1.544 +
   1.545 +bool
   1.546 +GlobalWorkerThreadState::canStartIonCompile()
   1.547 +{
   1.548 +    // A worker thread can begin an Ion compilation if (a) there is some script
   1.549 +    // which is waiting to be compiled, and (b) no other worker thread is
   1.550 +    // currently compiling a script. The latter condition ensures that two
   1.551 +    // compilations cannot simultaneously occur.
   1.552 +    if (ionWorklist().empty())
   1.553 +        return false;
   1.554 +    for (size_t i = 0; i < threadCount; i++) {
   1.555 +        if (threads[i].ionBuilder)
   1.556 +            return false;
   1.557 +    }
   1.558 +    return true;
   1.559 +}
   1.560 +
   1.561 +bool
   1.562 +GlobalWorkerThreadState::canStartParseTask()
   1.563 +{
   1.564 +    // Don't allow simultaneous off thread parses, to reduce contention on the
   1.565 +    // atoms table. Note that asm.js compilation depends on this to avoid
   1.566 +    // stalling the worker thread, as off thread parse tasks can trigger and
   1.567 +    // block on other off thread asm.js compilation tasks.
   1.568 +    JS_ASSERT(isLocked());
   1.569 +    if (parseWorklist().empty())
   1.570 +        return false;
   1.571 +    for (size_t i = 0; i < threadCount; i++) {
   1.572 +        if (threads[i].parseTask)
   1.573 +            return false;
   1.574 +    }
   1.575 +    return true;
   1.576 +}
   1.577 +
   1.578 +bool
   1.579 +GlobalWorkerThreadState::canStartCompressionTask()
   1.580 +{
   1.581 +    return !compressionWorklist().empty();
   1.582 +}
   1.583 +
   1.584 +static void
   1.585 +CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
   1.586 +{
   1.587 +    // We should never hit this, since nested scripts are also constructed via
   1.588 +    // BytecodeEmitter instances on the stack.
   1.589 +    JS_CHECK_RECURSION(cx, return);
   1.590 +
   1.591 +    // Recurse to any nested scripts.
   1.592 +    if (script->hasObjects()) {
   1.593 +        ObjectArray *objects = script->objects();
   1.594 +        for (size_t i = 0; i < objects->length; i++) {
   1.595 +            JSObject *obj = objects->vector[i];
   1.596 +            if (obj->is<JSFunction>()) {
   1.597 +                JSFunction *fun = &obj->as<JSFunction>();
   1.598 +                if (fun->hasScript()) {
   1.599 +                    RootedScript nested(cx, fun->nonLazyScript());
   1.600 +                    CallNewScriptHookForAllScripts(cx, nested);
   1.601 +                }
   1.602 +            }
   1.603 +        }
   1.604 +    }
   1.605 +
   1.606 +    // The global new script hook is called on every script that was compiled.
   1.607 +    RootedFunction function(cx, script->functionNonDelazifying());
   1.608 +    CallNewScriptHook(cx, script, function);
   1.609 +}
   1.610 +
   1.611 +static void
   1.612 +LeaveParseTaskZone(JSRuntime *rt, ParseTask *task)
   1.613 +{
   1.614 +    // Mark the zone as no longer in use by an ExclusiveContext, and available
   1.615 +    // to be collected by the GC.
   1.616 +    rt->clearUsedByExclusiveThread(task->cx->zone());
   1.617 +}
   1.618 +
   1.619 +JSScript *
   1.620 +GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
   1.621 +{
   1.622 +    ScopedJSDeletePtr<ParseTask> parseTask;
   1.623 +
   1.624 +    // The token is a ParseTask* which should be in the finished list.
   1.625 +    // Find and remove its entry.
   1.626 +    {
   1.627 +        AutoLockWorkerThreadState lock;
   1.628 +        ParseTaskVector &finished = parseFinishedList();
   1.629 +        for (size_t i = 0; i < finished.length(); i++) {
   1.630 +            if (finished[i] == token) {
   1.631 +                parseTask = finished[i];
   1.632 +                remove(finished, &i);
   1.633 +                break;
   1.634 +            }
   1.635 +        }
   1.636 +    }
   1.637 +    JS_ASSERT(parseTask);
   1.638 +
   1.639 +    if (!maybecx) {
   1.640 +        LeaveParseTaskZone(rt, parseTask);
   1.641 +        return nullptr;
   1.642 +    }
   1.643 +
   1.644 +    JSContext *cx = maybecx;
   1.645 +    JS_ASSERT(cx->compartment());
   1.646 +
   1.647 +    // Make sure we have all the constructors we need for the prototype
   1.648 +    // remapping below, since we can't GC while that's happening.
   1.649 +    Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
   1.650 +    if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object) ||
   1.651 +        !GlobalObject::ensureConstructor(cx, global, JSProto_Array) ||
   1.652 +        !GlobalObject::ensureConstructor(cx, global, JSProto_Function) ||
   1.653 +        !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) ||
   1.654 +        !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator))
   1.655 +    {
   1.656 +        LeaveParseTaskZone(rt, parseTask);
   1.657 +        return nullptr;
   1.658 +    }
   1.659 +
   1.660 +    LeaveParseTaskZone(rt, parseTask);
   1.661 +
   1.662 +    // Point the prototypes of any objects in the script's compartment to refer
   1.663 +    // to the corresponding prototype in the new compartment. This will briefly
   1.664 +    // create cross compartment pointers, which will be fixed by the
   1.665 +    // MergeCompartments call below.
   1.666 +    for (gc::CellIter iter(parseTask->cx->zone(), gc::FINALIZE_TYPE_OBJECT);
   1.667 +         !iter.done();
   1.668 +         iter.next())
   1.669 +    {
   1.670 +        types::TypeObject *object = iter.get<types::TypeObject>();
   1.671 +        TaggedProto proto(object->proto());
   1.672 +        if (!proto.isObject())
   1.673 +            continue;
   1.674 +
   1.675 +        JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
   1.676 +        if (key == JSProto_Null)
   1.677 +            continue;
   1.678 +        JS_ASSERT(key == JSProto_Object || key == JSProto_Array ||
   1.679 +                  key == JSProto_Function || key == JSProto_RegExp ||
   1.680 +                  key == JSProto_Iterator);
   1.681 +
   1.682 +        JSObject *newProto = GetBuiltinPrototypePure(global, key);
   1.683 +        JS_ASSERT(newProto);
   1.684 +
   1.685 +        object->setProtoUnchecked(newProto);
   1.686 +    }
   1.687 +
   1.688 +    // Move the parsed script and all its contents into the desired compartment.
   1.689 +    gc::MergeCompartments(parseTask->cx->compartment(), cx->compartment());
   1.690 +    parseTask->finish();
   1.691 +
   1.692 +    RootedScript script(rt, parseTask->script);
   1.693 +    assertSameCompartment(cx, script);
   1.694 +
   1.695 +    // Report any error or warnings generated during the parse, and inform the
   1.696 +    // debugger about the compiled scripts.
   1.697 +    for (size_t i = 0; i < parseTask->errors.length(); i++)
   1.698 +        parseTask->errors[i]->throwError(cx);
   1.699 +    if (parseTask->overRecursed)
   1.700 +        js_ReportOverRecursed(cx);
   1.701 +
   1.702 +    if (script) {
   1.703 +        // The Debugger only needs to be told about the topmost script that was compiled.
   1.704 +        GlobalObject *compileAndGoGlobal = nullptr;
   1.705 +        if (script->compileAndGo())
   1.706 +            compileAndGoGlobal = &script->global();
   1.707 +        Debugger::onNewScript(cx, script, compileAndGoGlobal);
   1.708 +
   1.709 +        // The NewScript hook needs to be called for all compiled scripts.
   1.710 +        CallNewScriptHookForAllScripts(cx, script);
   1.711 +    }
   1.712 +
   1.713 +    return script;
   1.714 +}
   1.715 +
   1.716 +void
   1.717 +WorkerThread::destroy()
   1.718 +{
   1.719 +    if (thread) {
   1.720 +        {
   1.721 +            AutoLockWorkerThreadState lock;
   1.722 +            terminate = true;
   1.723 +
   1.724 +            /* Notify all workers, to ensure that this thread wakes up. */
   1.725 +            WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
   1.726 +        }
   1.727 +
   1.728 +        PR_JoinThread(thread);
   1.729 +    }
   1.730 +
   1.731 +    if (!threadData.empty())
   1.732 +        threadData.destroy();
   1.733 +}
   1.734 +
   1.735 +/* static */
   1.736 +void
   1.737 +WorkerThread::ThreadMain(void *arg)
   1.738 +{
   1.739 +    PR_SetCurrentThreadName("Analysis Helper");
   1.740 +    static_cast<WorkerThread *>(arg)->threadLoop();
   1.741 +}
   1.742 +
   1.743 +void
   1.744 +WorkerThread::handleAsmJSWorkload()
   1.745 +{
   1.746 +#ifdef JS_ION
   1.747 +    JS_ASSERT(WorkerThreadState().isLocked());
   1.748 +    JS_ASSERT(WorkerThreadState().canStartAsmJSCompile());
   1.749 +    JS_ASSERT(idle());
   1.750 +
   1.751 +    asmData = WorkerThreadState().asmJSWorklist().popCopy();
   1.752 +    bool success = false;
   1.753 +
   1.754 +    do {
   1.755 +        AutoUnlockWorkerThreadState unlock;
   1.756 +        PerThreadData::AutoEnterRuntime enter(threadData.addr(), asmData->runtime);
   1.757 +
   1.758 +        jit::IonContext icx(asmData->mir->compartment->runtime(),
   1.759 +                            asmData->mir->compartment,
   1.760 +                            &asmData->mir->alloc());
   1.761 +
   1.762 +        int64_t before = PRMJ_Now();
   1.763 +
   1.764 +        if (!OptimizeMIR(asmData->mir))
   1.765 +            break;
   1.766 +
   1.767 +        asmData->lir = GenerateLIR(asmData->mir);
   1.768 +        if (!asmData->lir)
   1.769 +            break;
   1.770 +
   1.771 +        int64_t after = PRMJ_Now();
   1.772 +        asmData->compileTime = (after - before) / PRMJ_USEC_PER_MSEC;
   1.773 +
   1.774 +        success = true;
   1.775 +    } while(0);
   1.776 +
   1.777 +    // On failure, signal parent for harvesting in CancelOutstandingJobs().
   1.778 +    if (!success) {
   1.779 +        WorkerThreadState().noteAsmJSFailure(asmData->func);
   1.780 +        WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
   1.781 +        asmData = nullptr;
   1.782 +        return;
   1.783 +    }
   1.784 +
   1.785 +    // On success, move work to the finished list.
   1.786 +    WorkerThreadState().asmJSFinishedList().append(asmData);
   1.787 +    asmData = nullptr;
   1.788 +
   1.789 +    // Notify the main thread in case it's blocked waiting for a LifoAlloc.
   1.790 +    WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
   1.791 +#else
   1.792 +    MOZ_CRASH();
   1.793 +#endif // JS_ION
   1.794 +}
   1.795 +
   1.796 +void
   1.797 +WorkerThread::handleIonWorkload()
   1.798 +{
   1.799 +#ifdef JS_ION
   1.800 +    JS_ASSERT(WorkerThreadState().isLocked());
   1.801 +    JS_ASSERT(WorkerThreadState().canStartIonCompile());
   1.802 +    JS_ASSERT(idle());
   1.803 +
   1.804 +    ionBuilder = WorkerThreadState().ionWorklist().popCopy();
   1.805 +
   1.806 +    TraceLogger *logger = TraceLoggerForCurrentThread();
   1.807 +    AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, ionBuilder->script()));
   1.808 +    AutoTraceLog logCompile(logger, TraceLogger::IonCompilation);
   1.809 +
   1.810 +    JSRuntime *rt = ionBuilder->script()->compartment()->runtimeFromAnyThread();
   1.811 +
   1.812 +    {
   1.813 +        AutoUnlockWorkerThreadState unlock;
   1.814 +        PerThreadData::AutoEnterRuntime enter(threadData.addr(),
   1.815 +                                              ionBuilder->script()->runtimeFromAnyThread());
   1.816 +        jit::IonContext ictx(jit::CompileRuntime::get(rt),
   1.817 +                             jit::CompileCompartment::get(ionBuilder->script()->compartment()),
   1.818 +                             &ionBuilder->alloc());
   1.819 +        ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
   1.820 +    }
   1.821 +
   1.822 +    FinishOffThreadIonCompile(ionBuilder);
   1.823 +    ionBuilder = nullptr;
   1.824 +
   1.825 +    // Ping the main thread so that the compiled code can be incorporated
   1.826 +    // at the next interrupt callback. Don't interrupt Ion code for this, as
   1.827 +    // this incorporation can be delayed indefinitely without affecting
   1.828 +    // performance as long as the main thread is actually executing Ion code.
   1.829 +    rt->requestInterrupt(JSRuntime::RequestInterruptAnyThreadDontStopIon);
   1.830 +
   1.831 +    // Notify the main thread in case it is waiting for the compilation to finish.
   1.832 +    WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
   1.833 +#else
   1.834 +    MOZ_CRASH();
   1.835 +#endif // JS_ION
   1.836 +}
   1.837 +
   1.838 +void
   1.839 +ExclusiveContext::setWorkerThread(WorkerThread *workerThread)
   1.840 +{
   1.841 +    workerThread_ = workerThread;
   1.842 +    perThreadData = workerThread->threadData.addr();
   1.843 +}
   1.844 +
   1.845 +frontend::CompileError &
   1.846 +ExclusiveContext::addPendingCompileError()
   1.847 +{
   1.848 +    frontend::CompileError *error = js_new<frontend::CompileError>();
   1.849 +    if (!error)
   1.850 +        MOZ_CRASH();
   1.851 +    if (!workerThread()->parseTask->errors.append(error))
   1.852 +        MOZ_CRASH();
   1.853 +    return *error;
   1.854 +}
   1.855 +
   1.856 +void
   1.857 +ExclusiveContext::addPendingOverRecursed()
   1.858 +{
   1.859 +    if (workerThread()->parseTask)
   1.860 +        workerThread()->parseTask->overRecursed = true;
   1.861 +}
   1.862 +
   1.863 +void
   1.864 +WorkerThread::handleParseWorkload()
   1.865 +{
   1.866 +    JS_ASSERT(WorkerThreadState().isLocked());
   1.867 +    JS_ASSERT(WorkerThreadState().canStartParseTask());
   1.868 +    JS_ASSERT(idle());
   1.869 +
   1.870 +    parseTask = WorkerThreadState().parseWorklist().popCopy();
   1.871 +    parseTask->cx->setWorkerThread(this);
   1.872 +
   1.873 +    {
   1.874 +        AutoUnlockWorkerThreadState unlock;
   1.875 +        PerThreadData::AutoEnterRuntime enter(threadData.addr(),
   1.876 +                                              parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
   1.877 +        SourceBufferHolder srcBuf(parseTask->chars, parseTask->length,
   1.878 +                                  SourceBufferHolder::NoOwnership);
   1.879 +        parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
   1.880 +                                                    NullPtr(), NullPtr(),
   1.881 +                                                    parseTask->options,
   1.882 +                                                    srcBuf);
   1.883 +    }
   1.884 +
   1.885 +    // The callback is invoked while we are still off the main thread.
   1.886 +    parseTask->callback(parseTask, parseTask->callbackData);
   1.887 +
   1.888 +    // FinishOffThreadScript will need to be called on the script to
   1.889 +    // migrate it into the correct compartment.
   1.890 +    WorkerThreadState().parseFinishedList().append(parseTask);
   1.891 +
   1.892 +    parseTask = nullptr;
   1.893 +
   1.894 +    // Notify the main thread in case it is waiting for the parse/emit to finish.
   1.895 +    WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
   1.896 +}
   1.897 +
   1.898 +void
   1.899 +WorkerThread::handleCompressionWorkload()
   1.900 +{
   1.901 +    JS_ASSERT(WorkerThreadState().isLocked());
   1.902 +    JS_ASSERT(WorkerThreadState().canStartCompressionTask());
   1.903 +    JS_ASSERT(idle());
   1.904 +
   1.905 +    compressionTask = WorkerThreadState().compressionWorklist().popCopy();
   1.906 +    compressionTask->workerThread = this;
   1.907 +
   1.908 +    {
   1.909 +        AutoUnlockWorkerThreadState unlock;
   1.910 +        if (!compressionTask->work())
   1.911 +            compressionTask->setOOM();
   1.912 +    }
   1.913 +
   1.914 +    compressionTask->workerThread = nullptr;
   1.915 +    compressionTask = nullptr;
   1.916 +
   1.917 +    // Notify the main thread in case it is waiting for the compression to finish.
   1.918 +    WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
   1.919 +}
   1.920 +
   1.921 +bool
   1.922 +js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
   1.923 +{
   1.924 +    EnsureWorkerThreadsInitialized(cx);
   1.925 +
   1.926 +    AutoLockWorkerThreadState lock;
   1.927 +
   1.928 +    if (!WorkerThreadState().compressionWorklist().append(task))
   1.929 +        return false;
   1.930 +
   1.931 +    WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
   1.932 +    return true;
   1.933 +}
   1.934 +
   1.935 +bool
   1.936 +GlobalWorkerThreadState::compressionInProgress(SourceCompressionTask *task)
   1.937 +{
   1.938 +    JS_ASSERT(isLocked());
   1.939 +    for (size_t i = 0; i < compressionWorklist().length(); i++) {
   1.940 +        if (compressionWorklist()[i] == task)
   1.941 +            return true;
   1.942 +    }
   1.943 +    for (size_t i = 0; i < threadCount; i++) {
   1.944 +        if (threads[i].compressionTask == task)
   1.945 +            return true;
   1.946 +    }
   1.947 +    return false;
   1.948 +}
   1.949 +
   1.950 +bool
   1.951 +SourceCompressionTask::complete()
   1.952 +{
   1.953 +    JS_ASSERT_IF(!ss, !chars);
   1.954 +    if (active()) {
   1.955 +        AutoLockWorkerThreadState lock;
   1.956 +
   1.957 +        while (WorkerThreadState().compressionInProgress(this))
   1.958 +            WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
   1.959 +
   1.960 +        ss->ready_ = true;
   1.961 +
   1.962 +        // Update memory accounting.
   1.963 +        if (!oom)
   1.964 +            cx->updateMallocCounter(ss->computedSizeOfData());
   1.965 +
   1.966 +        ss = nullptr;
   1.967 +        chars = nullptr;
   1.968 +    }
   1.969 +    if (oom) {
   1.970 +        js_ReportOutOfMemory(cx);
   1.971 +        return false;
   1.972 +    }
   1.973 +    return true;
   1.974 +}
   1.975 +
   1.976 +SourceCompressionTask *
   1.977 +GlobalWorkerThreadState::compressionTaskForSource(ScriptSource *ss)
   1.978 +{
   1.979 +    JS_ASSERT(isLocked());
   1.980 +    for (size_t i = 0; i < compressionWorklist().length(); i++) {
   1.981 +        SourceCompressionTask *task = compressionWorklist()[i];
   1.982 +        if (task->source() == ss)
   1.983 +            return task;
   1.984 +    }
   1.985 +    for (size_t i = 0; i < threadCount; i++) {
   1.986 +        SourceCompressionTask *task = threads[i].compressionTask;
   1.987 +        if (task && task->source() == ss)
   1.988 +            return task;
   1.989 +    }
   1.990 +    return nullptr;
   1.991 +}
   1.992 +
   1.993 +const jschar *
   1.994 +ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
   1.995 +{
   1.996 +    // If this is being compressed off thread, return its uncompressed chars.
   1.997 +
   1.998 +    if (ready()) {
   1.999 +        // Compression has already finished on the source.
  1.1000 +        return nullptr;
  1.1001 +    }
  1.1002 +
  1.1003 +    AutoLockWorkerThreadState lock;
  1.1004 +
  1.1005 +    // Look for a token that hasn't finished compressing and whose source is
  1.1006 +    // the given ScriptSource.
  1.1007 +    if (SourceCompressionTask *task = WorkerThreadState().compressionTaskForSource(this))
  1.1008 +        return task->uncompressedChars();
  1.1009 +
  1.1010 +    // Compressing has finished, so this ScriptSource is ready. Avoid future
  1.1011 +    // queries on the worker thread state when getting the chars.
  1.1012 +    ready_ = true;
  1.1013 +
  1.1014 +    return nullptr;
  1.1015 +}
  1.1016 +
  1.1017 +void
  1.1018 +WorkerThread::threadLoop()
  1.1019 +{
  1.1020 +    JS::AutoAssertNoGC nogc;
  1.1021 +    AutoLockWorkerThreadState lock;
  1.1022 +
  1.1023 +    js::TlsPerThreadData.set(threadData.addr());
  1.1024 +
  1.1025 +    // Compute the thread's stack limit, for over-recursed checks.
  1.1026 +    uintptr_t stackLimit = GetNativeStackBase();
  1.1027 +#if JS_STACK_GROWTH_DIRECTION > 0
  1.1028 +    stackLimit += WORKER_STACK_QUOTA;
  1.1029 +#else
  1.1030 +    stackLimit -= WORKER_STACK_QUOTA;
  1.1031 +#endif
  1.1032 +    for (size_t i = 0; i < ArrayLength(threadData.ref().nativeStackLimit); i++)
  1.1033 +        threadData.ref().nativeStackLimit[i] = stackLimit;
  1.1034 +
  1.1035 +    while (true) {
  1.1036 +        JS_ASSERT(!ionBuilder && !asmData);
  1.1037 +
  1.1038 +        // Block until a task is available.
  1.1039 +        while (true) {
  1.1040 +            if (terminate)
  1.1041 +                return;
  1.1042 +            if (WorkerThreadState().canStartIonCompile() ||
  1.1043 +                WorkerThreadState().canStartAsmJSCompile() ||
  1.1044 +                WorkerThreadState().canStartParseTask() ||
  1.1045 +                WorkerThreadState().canStartCompressionTask())
  1.1046 +            {
  1.1047 +                break;
  1.1048 +            }
  1.1049 +            WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
  1.1050 +        }
  1.1051 +
  1.1052 +        // Dispatch tasks, prioritizing AsmJS work.
  1.1053 +        if (WorkerThreadState().canStartAsmJSCompile())
  1.1054 +            handleAsmJSWorkload();
  1.1055 +        else if (WorkerThreadState().canStartIonCompile())
  1.1056 +            handleIonWorkload();
  1.1057 +        else if (WorkerThreadState().canStartParseTask())
  1.1058 +            handleParseWorkload();
  1.1059 +        else if (WorkerThreadState().canStartCompressionTask())
  1.1060 +            handleCompressionWorkload();
  1.1061 +        else
  1.1062 +            MOZ_ASSUME_UNREACHABLE("No task to perform");
  1.1063 +    }
  1.1064 +}
  1.1065 +
  1.1066 +#else /* JS_THREADSAFE */
  1.1067 +
  1.1068 +using namespace js;
  1.1069 +
  1.1070 +#ifdef JS_ION
  1.1071 +
  1.1072 +bool
  1.1073 +js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
  1.1074 +{
  1.1075 +    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
  1.1076 +}
  1.1077 +
  1.1078 +bool
  1.1079 +js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
  1.1080 +{
  1.1081 +    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
  1.1082 +}
  1.1083 +
  1.1084 +#endif // JS_ION
  1.1085 +
  1.1086 +void
  1.1087 +js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
  1.1088 +{
  1.1089 +}
  1.1090 +
  1.1091 +void
  1.1092 +js::CancelOffThreadParses(JSRuntime *rt)
  1.1093 +{
  1.1094 +}
  1.1095 +
  1.1096 +bool
  1.1097 +js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
  1.1098 +                              const jschar *chars, size_t length,
  1.1099 +                              JS::OffThreadCompileCallback callback, void *callbackData)
  1.1100 +{
  1.1101 +    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
  1.1102 +}
  1.1103 +
  1.1104 +bool
  1.1105 +js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
  1.1106 +{
  1.1107 +    MOZ_ASSUME_UNREACHABLE("Off thread compression not available");
  1.1108 +}
  1.1109 +
  1.1110 +bool
  1.1111 +SourceCompressionTask::complete()
  1.1112 +{
  1.1113 +    JS_ASSERT(!active() && !oom);
  1.1114 +    return true;
  1.1115 +}
  1.1116 +
  1.1117 +const jschar *
  1.1118 +ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
  1.1119 +{
  1.1120 +    JS_ASSERT(ready());
  1.1121 +    return nullptr;
  1.1122 +}
  1.1123 +
  1.1124 +frontend::CompileError &
  1.1125 +ExclusiveContext::addPendingCompileError()
  1.1126 +{
  1.1127 +    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
  1.1128 +}
  1.1129 +
  1.1130 +void
  1.1131 +ExclusiveContext::addPendingOverRecursed()
  1.1132 +{
  1.1133 +    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
  1.1134 +}
  1.1135 +
  1.1136 +#endif /* JS_THREADSAFE */

mercurial