js/src/jsworkers.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jsworkers.h"
michael@0 8
michael@0 9 #ifdef JS_THREADSAFE
michael@0 10
michael@0 11 #include "mozilla/DebugOnly.h"
michael@0 12
michael@0 13 #include "jsnativestack.h"
michael@0 14 #include "prmjtime.h"
michael@0 15
michael@0 16 #include "frontend/BytecodeCompiler.h"
michael@0 17 #include "jit/IonBuilder.h"
michael@0 18 #include "vm/Debugger.h"
michael@0 19 #include "vm/TraceLogging.h"
michael@0 20
michael@0 21 #include "jscntxtinlines.h"
michael@0 22 #include "jscompartmentinlines.h"
michael@0 23 #include "jsobjinlines.h"
michael@0 24 #include "jsscriptinlines.h"
michael@0 25
michael@0 26 using namespace js;
michael@0 27
michael@0 28 using mozilla::ArrayLength;
michael@0 29 using mozilla::DebugOnly;
michael@0 30
michael@0 31 namespace js {
michael@0 32
michael@0 33 GlobalWorkerThreadState gWorkerThreadState;
michael@0 34
michael@0 35 } // namespace js
michael@0 36
michael@0 37 void
michael@0 38 js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
michael@0 39 {
michael@0 40 // If 'cx' is not a JSContext, we are already off the main thread and the
michael@0 41 // worker threads would have already been initialized.
michael@0 42 if (!cx->isJSContext())
michael@0 43 return;
michael@0 44
michael@0 45 WorkerThreadState().ensureInitialized();
michael@0 46 }
michael@0 47
michael@0 48 static size_t
michael@0 49 ThreadCountForCPUCount(size_t cpuCount)
michael@0 50 {
michael@0 51 return Max(cpuCount, (size_t)2);
michael@0 52 }
michael@0 53
michael@0 54 void
michael@0 55 js::SetFakeCPUCount(size_t count)
michael@0 56 {
michael@0 57 // This must be called before the threads have been initialized.
michael@0 58 JS_ASSERT(!WorkerThreadState().threads);
michael@0 59
michael@0 60 WorkerThreadState().cpuCount = count;
michael@0 61 WorkerThreadState().threadCount = ThreadCountForCPUCount(count);
michael@0 62 }
michael@0 63
michael@0 64 #ifdef JS_ION
michael@0 65
michael@0 66 bool
michael@0 67 js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
michael@0 68 {
michael@0 69 // Threads already initialized by the AsmJS compiler.
michael@0 70 JS_ASSERT(asmData->mir);
michael@0 71 JS_ASSERT(asmData->lir == nullptr);
michael@0 72
michael@0 73 AutoLockWorkerThreadState lock;
michael@0 74
michael@0 75 // Don't append this task if another failed.
michael@0 76 if (WorkerThreadState().asmJSWorkerFailed())
michael@0 77 return false;
michael@0 78
michael@0 79 if (!WorkerThreadState().asmJSWorklist().append(asmData))
michael@0 80 return false;
michael@0 81
michael@0 82 WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
michael@0 83 return true;
michael@0 84 }
michael@0 85
michael@0 86 bool
michael@0 87 js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
michael@0 88 {
michael@0 89 EnsureWorkerThreadsInitialized(cx);
michael@0 90
michael@0 91 AutoLockWorkerThreadState lock;
michael@0 92
michael@0 93 if (!WorkerThreadState().ionWorklist().append(builder))
michael@0 94 return false;
michael@0 95
michael@0 96 WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
michael@0 97 return true;
michael@0 98 }
michael@0 99
michael@0 100 /*
michael@0 101 * Move an IonBuilder for which compilation has either finished, failed, or
michael@0 102 * been cancelled into the global finished compilation list. All off thread
michael@0 103 * compilations which are started must eventually be finished.
michael@0 104 */
michael@0 105 static void
michael@0 106 FinishOffThreadIonCompile(jit::IonBuilder *builder)
michael@0 107 {
michael@0 108 WorkerThreadState().ionFinishedList().append(builder);
michael@0 109 }
michael@0 110
michael@0 111 #endif // JS_ION
michael@0 112
michael@0 113 static inline bool
michael@0 114 CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
michael@0 115 {
michael@0 116 if (script)
michael@0 117 return target == script;
michael@0 118 return target->compartment() == compartment;
michael@0 119 }
michael@0 120
michael@0 121 void
michael@0 122 js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
michael@0 123 {
michael@0 124 #ifdef JS_ION
michael@0 125 jit::JitCompartment *jitComp = compartment->jitCompartment();
michael@0 126 if (!jitComp)
michael@0 127 return;
michael@0 128
michael@0 129 AutoLockWorkerThreadState lock;
michael@0 130
michael@0 131 if (!WorkerThreadState().threads)
michael@0 132 return;
michael@0 133
michael@0 134 /* Cancel any pending entries for which processing hasn't started. */
michael@0 135 GlobalWorkerThreadState::IonBuilderVector &worklist = WorkerThreadState().ionWorklist();
michael@0 136 for (size_t i = 0; i < worklist.length(); i++) {
michael@0 137 jit::IonBuilder *builder = worklist[i];
michael@0 138 if (CompiledScriptMatches(compartment, script, builder->script())) {
michael@0 139 FinishOffThreadIonCompile(builder);
michael@0 140 WorkerThreadState().remove(worklist, &i);
michael@0 141 }
michael@0 142 }
michael@0 143
michael@0 144 /* Wait for in progress entries to finish up. */
michael@0 145 for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
michael@0 146 const WorkerThread &helper = WorkerThreadState().threads[i];
michael@0 147 while (helper.ionBuilder &&
michael@0 148 CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
michael@0 149 {
michael@0 150 helper.ionBuilder->cancel();
michael@0 151 WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
michael@0 152 }
michael@0 153 }
michael@0 154
michael@0 155 /* Cancel code generation for any completed entries. */
michael@0 156 GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
michael@0 157 for (size_t i = 0; i < finished.length(); i++) {
michael@0 158 jit::IonBuilder *builder = finished[i];
michael@0 159 if (CompiledScriptMatches(compartment, script, builder->script())) {
michael@0 160 jit::FinishOffThreadBuilder(builder);
michael@0 161 WorkerThreadState().remove(finished, &i);
michael@0 162 }
michael@0 163 }
michael@0 164 #endif // JS_ION
michael@0 165 }
michael@0 166
michael@0 167 static const JSClass workerGlobalClass = {
michael@0 168 "internal-worker-global", JSCLASS_GLOBAL_FLAGS,
michael@0 169 JS_PropertyStub, JS_DeletePropertyStub,
michael@0 170 JS_PropertyStub, JS_StrictPropertyStub,
michael@0 171 JS_EnumerateStub, JS_ResolveStub,
michael@0 172 JS_ConvertStub, nullptr,
michael@0 173 nullptr, nullptr, nullptr,
michael@0 174 JS_GlobalObjectTraceHook
michael@0 175 };
michael@0 176
michael@0 177 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
michael@0 178 const jschar *chars, size_t length,
michael@0 179 JS::OffThreadCompileCallback callback, void *callbackData)
michael@0 180 : cx(cx), options(initCx), chars(chars), length(length),
michael@0 181 alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
michael@0 182 exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
michael@0 183 optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
michael@0 184 script(nullptr), errors(cx), overRecursed(false)
michael@0 185 {
michael@0 186 }
michael@0 187
michael@0 188 bool
michael@0 189 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
michael@0 190 {
michael@0 191 if (!this->options.copy(cx, options))
michael@0 192 return false;
michael@0 193
michael@0 194 // Save those compilation options that the ScriptSourceObject can't
michael@0 195 // point at while it's in the compilation's temporary compartment.
michael@0 196 optionsElement = this->options.element();
michael@0 197 this->options.setElement(nullptr);
michael@0 198 optionsIntroductionScript = this->options.introductionScript();
michael@0 199 this->options.setIntroductionScript(nullptr);
michael@0 200
michael@0 201 return true;
michael@0 202 }
michael@0 203
michael@0 204 void
michael@0 205 ParseTask::activate(JSRuntime *rt)
michael@0 206 {
michael@0 207 rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
michael@0 208 cx->enterCompartment(exclusiveContextGlobal->compartment());
michael@0 209 }
michael@0 210
michael@0 211 void
michael@0 212 ParseTask::finish()
michael@0 213 {
michael@0 214 if (script) {
michael@0 215 // Initialize the ScriptSourceObject slots that we couldn't while the SSO
michael@0 216 // was in the temporary compartment.
michael@0 217 ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
michael@0 218 sso.initElement(optionsElement);
michael@0 219 sso.initIntroductionScript(optionsIntroductionScript);
michael@0 220 }
michael@0 221 }
michael@0 222
michael@0 223 ParseTask::~ParseTask()
michael@0 224 {
michael@0 225 // ParseTask takes over ownership of its input exclusive context.
michael@0 226 js_delete(cx);
michael@0 227
michael@0 228 for (size_t i = 0; i < errors.length(); i++)
michael@0 229 js_delete(errors[i]);
michael@0 230 }
michael@0 231
michael@0 232 void
michael@0 233 js::CancelOffThreadParses(JSRuntime *rt)
michael@0 234 {
michael@0 235 AutoLockWorkerThreadState lock;
michael@0 236
michael@0 237 if (!WorkerThreadState().threads)
michael@0 238 return;
michael@0 239
michael@0 240 // Instead of forcibly canceling pending parse tasks, just wait for all scheduled
michael@0 241 // and in progress ones to complete. Otherwise the final GC may not collect
michael@0 242 // everything due to zones being used off thread.
michael@0 243 while (true) {
michael@0 244 bool pending = false;
michael@0 245 GlobalWorkerThreadState::ParseTaskVector &worklist = WorkerThreadState().parseWorklist();
michael@0 246 for (size_t i = 0; i < worklist.length(); i++) {
michael@0 247 ParseTask *task = worklist[i];
michael@0 248 if (task->runtimeMatches(rt))
michael@0 249 pending = true;
michael@0 250 }
michael@0 251 if (!pending) {
michael@0 252 bool inProgress = false;
michael@0 253 for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
michael@0 254 ParseTask *task = WorkerThreadState().threads[i].parseTask;
michael@0 255 if (task && task->runtimeMatches(rt))
michael@0 256 inProgress = true;
michael@0 257 }
michael@0 258 if (!inProgress)
michael@0 259 break;
michael@0 260 }
michael@0 261 WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
michael@0 262 }
michael@0 263
michael@0 264 // Clean up any parse tasks which haven't been finished by the main thread.
michael@0 265 GlobalWorkerThreadState::ParseTaskVector &finished = WorkerThreadState().parseFinishedList();
michael@0 266 while (true) {
michael@0 267 bool found = false;
michael@0 268 for (size_t i = 0; i < finished.length(); i++) {
michael@0 269 ParseTask *task = finished[i];
michael@0 270 if (task->runtimeMatches(rt)) {
michael@0 271 found = true;
michael@0 272 AutoUnlockWorkerThreadState unlock;
michael@0 273 WorkerThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task);
michael@0 274 }
michael@0 275 }
michael@0 276 if (!found)
michael@0 277 break;
michael@0 278 }
michael@0 279 }
michael@0 280
michael@0 281 bool
michael@0 282 js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
michael@0 283 {
michael@0 284 // Off thread parsing can't occur during incremental collections on the
michael@0 285 // atoms compartment, to avoid triggering barriers. (Outside the atoms
michael@0 286 // compartment, the compilation will use a new zone that is never
michael@0 287 // collected.) If an atoms-zone GC is in progress, hold off on executing the
michael@0 288 // parse task until the atoms-zone GC completes (see
michael@0 289 // EnqueuePendingParseTasksAfterGC).
michael@0 290 return rt->activeGCInAtomsZone();
michael@0 291 }
michael@0 292
michael@0 293 bool
michael@0 294 js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
michael@0 295 const jschar *chars, size_t length,
michael@0 296 JS::OffThreadCompileCallback callback, void *callbackData)
michael@0 297 {
michael@0 298 // Suppress GC so that calls below do not trigger a new incremental GC
michael@0 299 // which could require barriers on the atoms compartment.
michael@0 300 gc::AutoSuppressGC suppress(cx);
michael@0 301
michael@0 302 SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
michael@0 303 frontend::MaybeCallSourceHandler(cx, options, srcBuf);
michael@0 304
michael@0 305 EnsureWorkerThreadsInitialized(cx);
michael@0 306
michael@0 307 JS::CompartmentOptions compartmentOptions(cx->compartment()->options());
michael@0 308 compartmentOptions.setZone(JS::FreshZone);
michael@0 309 compartmentOptions.setInvisibleToDebugger(true);
michael@0 310 compartmentOptions.setMergeable(true);
michael@0 311
michael@0 312 // Don't falsely inherit the host's global trace hook.
michael@0 313 compartmentOptions.setTrace(nullptr);
michael@0 314
michael@0 315 JSObject *global = JS_NewGlobalObject(cx, &workerGlobalClass, nullptr,
michael@0 316 JS::FireOnNewGlobalHook, compartmentOptions);
michael@0 317 if (!global)
michael@0 318 return false;
michael@0 319
michael@0 320 JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals);
michael@0 321
michael@0 322 RootedObject obj(cx);
michael@0 323
michael@0 324 // Initialize all classes needed for parsing while we are still on the main
michael@0 325 // thread. Do this for both the target and the new global so that prototype
michael@0 326 // pointers can be changed infallibly after parsing finishes.
michael@0 327 if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
michael@0 328 !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
michael@0 329 !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
michael@0 330 !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
michael@0 331 {
michael@0 332 return false;
michael@0 333 }
michael@0 334 {
michael@0 335 AutoCompartment ac(cx, global);
michael@0 336 if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
michael@0 337 !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
michael@0 338 !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
michael@0 339 !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
michael@0 340 {
michael@0 341 return false;
michael@0 342 }
michael@0 343 }
michael@0 344
michael@0 345 ScopedJSDeletePtr<ExclusiveContext> workercx(
michael@0 346 cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData *) nullptr,
michael@0 347 ThreadSafeContext::Context_Exclusive));
michael@0 348 if (!workercx)
michael@0 349 return false;
michael@0 350
michael@0 351 ScopedJSDeletePtr<ParseTask> task(
michael@0 352 cx->new_<ParseTask>(workercx.get(), global, cx, chars, length,
michael@0 353 callback, callbackData));
michael@0 354 if (!task)
michael@0 355 return false;
michael@0 356
michael@0 357 workercx.forget();
michael@0 358
michael@0 359 if (!task->init(cx, options))
michael@0 360 return false;
michael@0 361
michael@0 362 if (OffThreadParsingMustWaitForGC(cx->runtime())) {
michael@0 363 AutoLockWorkerThreadState lock;
michael@0 364 if (!WorkerThreadState().parseWaitingOnGC().append(task.get()))
michael@0 365 return false;
michael@0 366 } else {
michael@0 367 task->activate(cx->runtime());
michael@0 368
michael@0 369 AutoLockWorkerThreadState lock;
michael@0 370
michael@0 371 if (!WorkerThreadState().parseWorklist().append(task.get()))
michael@0 372 return false;
michael@0 373
michael@0 374 WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
michael@0 375 }
michael@0 376
michael@0 377 task.forget();
michael@0 378
michael@0 379 return true;
michael@0 380 }
michael@0 381
michael@0 382 void
michael@0 383 js::EnqueuePendingParseTasksAfterGC(JSRuntime *rt)
michael@0 384 {
michael@0 385 JS_ASSERT(!OffThreadParsingMustWaitForGC(rt));
michael@0 386
michael@0 387 GlobalWorkerThreadState::ParseTaskVector newTasks;
michael@0 388 {
michael@0 389 AutoLockWorkerThreadState lock;
michael@0 390 GlobalWorkerThreadState::ParseTaskVector &waiting = WorkerThreadState().parseWaitingOnGC();
michael@0 391
michael@0 392 for (size_t i = 0; i < waiting.length(); i++) {
michael@0 393 ParseTask *task = waiting[i];
michael@0 394 if (task->runtimeMatches(rt)) {
michael@0 395 newTasks.append(task);
michael@0 396 WorkerThreadState().remove(waiting, &i);
michael@0 397 }
michael@0 398 }
michael@0 399 }
michael@0 400
michael@0 401 if (newTasks.empty())
michael@0 402 return;
michael@0 403
michael@0 404 // This logic should mirror the contents of the !activeGCInAtomsZone()
michael@0 405 // branch in StartOffThreadParseScript:
michael@0 406
michael@0 407 for (size_t i = 0; i < newTasks.length(); i++)
michael@0 408 newTasks[i]->activate(rt);
michael@0 409
michael@0 410 AutoLockWorkerThreadState lock;
michael@0 411
michael@0 412 for (size_t i = 0; i < newTasks.length(); i++)
michael@0 413 WorkerThreadState().parseWorklist().append(newTasks[i]);
michael@0 414
michael@0 415 WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
michael@0 416 }
michael@0 417
michael@0 418 static const uint32_t WORKER_STACK_SIZE = 512 * 1024;
michael@0 419 static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
michael@0 420
michael@0 421 void
michael@0 422 GlobalWorkerThreadState::ensureInitialized()
michael@0 423 {
michael@0 424 JS_ASSERT(this == &WorkerThreadState());
michael@0 425 AutoLockWorkerThreadState lock;
michael@0 426
michael@0 427 if (threads)
michael@0 428 return;
michael@0 429
michael@0 430 threads = js_pod_calloc<WorkerThread>(threadCount);
michael@0 431 if (!threads)
michael@0 432 CrashAtUnhandlableOOM("GlobalWorkerThreadState::ensureInitialized");
michael@0 433
michael@0 434 for (size_t i = 0; i < threadCount; i++) {
michael@0 435 WorkerThread &helper = threads[i];
michael@0 436 helper.threadData.construct(static_cast<JSRuntime *>(nullptr));
michael@0 437 helper.thread = PR_CreateThread(PR_USER_THREAD,
michael@0 438 WorkerThread::ThreadMain, &helper,
michael@0 439 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, WORKER_STACK_SIZE);
michael@0 440 if (!helper.thread || !helper.threadData.ref().init())
michael@0 441 CrashAtUnhandlableOOM("GlobalWorkerThreadState::ensureInitialized");
michael@0 442 }
michael@0 443
michael@0 444 resetAsmJSFailureState();
michael@0 445 }
michael@0 446
michael@0 447 GlobalWorkerThreadState::GlobalWorkerThreadState()
michael@0 448 {
michael@0 449 mozilla::PodZero(this);
michael@0 450
michael@0 451 cpuCount = GetCPUCount();
michael@0 452 threadCount = ThreadCountForCPUCount(cpuCount);
michael@0 453
michael@0 454 MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
michael@0 455
michael@0 456 workerLock = PR_NewLock();
michael@0 457 consumerWakeup = PR_NewCondVar(workerLock);
michael@0 458 producerWakeup = PR_NewCondVar(workerLock);
michael@0 459 }
michael@0 460
michael@0 461 void
michael@0 462 GlobalWorkerThreadState::finish()
michael@0 463 {
michael@0 464 if (threads) {
michael@0 465 for (size_t i = 0; i < threadCount; i++)
michael@0 466 threads[i].destroy();
michael@0 467 js_free(threads);
michael@0 468 }
michael@0 469
michael@0 470 PR_DestroyCondVar(consumerWakeup);
michael@0 471 PR_DestroyCondVar(producerWakeup);
michael@0 472 PR_DestroyLock(workerLock);
michael@0 473 }
michael@0 474
michael@0 475 void
michael@0 476 GlobalWorkerThreadState::lock()
michael@0 477 {
michael@0 478 JS_ASSERT(!isLocked());
michael@0 479 AssertCurrentThreadCanLock(WorkerThreadStateLock);
michael@0 480 PR_Lock(workerLock);
michael@0 481 #ifdef DEBUG
michael@0 482 lockOwner = PR_GetCurrentThread();
michael@0 483 #endif
michael@0 484 }
michael@0 485
michael@0 486 void
michael@0 487 GlobalWorkerThreadState::unlock()
michael@0 488 {
michael@0 489 JS_ASSERT(isLocked());
michael@0 490 #ifdef DEBUG
michael@0 491 lockOwner = nullptr;
michael@0 492 #endif
michael@0 493 PR_Unlock(workerLock);
michael@0 494 }
michael@0 495
michael@0 496 #ifdef DEBUG
michael@0 497 bool
michael@0 498 GlobalWorkerThreadState::isLocked()
michael@0 499 {
michael@0 500 return lockOwner == PR_GetCurrentThread();
michael@0 501 }
michael@0 502 #endif
michael@0 503
michael@0 504 void
michael@0 505 GlobalWorkerThreadState::wait(CondVar which, uint32_t millis)
michael@0 506 {
michael@0 507 JS_ASSERT(isLocked());
michael@0 508 #ifdef DEBUG
michael@0 509 lockOwner = nullptr;
michael@0 510 #endif
michael@0 511 DebugOnly<PRStatus> status =
michael@0 512 PR_WaitCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup,
michael@0 513 millis ? PR_MillisecondsToInterval(millis) : PR_INTERVAL_NO_TIMEOUT);
michael@0 514 JS_ASSERT(status == PR_SUCCESS);
michael@0 515 #ifdef DEBUG
michael@0 516 lockOwner = PR_GetCurrentThread();
michael@0 517 #endif
michael@0 518 }
michael@0 519
michael@0 520 void
michael@0 521 GlobalWorkerThreadState::notifyAll(CondVar which)
michael@0 522 {
michael@0 523 JS_ASSERT(isLocked());
michael@0 524 PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
michael@0 525 }
michael@0 526
michael@0 527 void
michael@0 528 GlobalWorkerThreadState::notifyOne(CondVar which)
michael@0 529 {
michael@0 530 JS_ASSERT(isLocked());
michael@0 531 PR_NotifyCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
michael@0 532 }
michael@0 533
michael@0 534 bool
michael@0 535 GlobalWorkerThreadState::canStartAsmJSCompile()
michael@0 536 {
michael@0 537 // Don't execute an AsmJS job if an earlier one failed.
michael@0 538 JS_ASSERT(isLocked());
michael@0 539 return !asmJSWorklist().empty() && !numAsmJSFailedJobs;
michael@0 540 }
michael@0 541
michael@0 542 bool
michael@0 543 GlobalWorkerThreadState::canStartIonCompile()
michael@0 544 {
michael@0 545 // A worker thread can begin an Ion compilation if (a) there is some script
michael@0 546 // which is waiting to be compiled, and (b) no other worker thread is
michael@0 547 // currently compiling a script. The latter condition ensures that two
michael@0 548 // compilations cannot simultaneously occur.
michael@0 549 if (ionWorklist().empty())
michael@0 550 return false;
michael@0 551 for (size_t i = 0; i < threadCount; i++) {
michael@0 552 if (threads[i].ionBuilder)
michael@0 553 return false;
michael@0 554 }
michael@0 555 return true;
michael@0 556 }
michael@0 557
michael@0 558 bool
michael@0 559 GlobalWorkerThreadState::canStartParseTask()
michael@0 560 {
michael@0 561 // Don't allow simultaneous off thread parses, to reduce contention on the
michael@0 562 // atoms table. Note that asm.js compilation depends on this to avoid
michael@0 563 // stalling the worker thread, as off thread parse tasks can trigger and
michael@0 564 // block on other off thread asm.js compilation tasks.
michael@0 565 JS_ASSERT(isLocked());
michael@0 566 if (parseWorklist().empty())
michael@0 567 return false;
michael@0 568 for (size_t i = 0; i < threadCount; i++) {
michael@0 569 if (threads[i].parseTask)
michael@0 570 return false;
michael@0 571 }
michael@0 572 return true;
michael@0 573 }
michael@0 574
michael@0 575 bool
michael@0 576 GlobalWorkerThreadState::canStartCompressionTask()
michael@0 577 {
michael@0 578 return !compressionWorklist().empty();
michael@0 579 }
michael@0 580
michael@0 581 static void
michael@0 582 CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
michael@0 583 {
michael@0 584 // We should never hit this, since nested scripts are also constructed via
michael@0 585 // BytecodeEmitter instances on the stack.
michael@0 586 JS_CHECK_RECURSION(cx, return);
michael@0 587
michael@0 588 // Recurse to any nested scripts.
michael@0 589 if (script->hasObjects()) {
michael@0 590 ObjectArray *objects = script->objects();
michael@0 591 for (size_t i = 0; i < objects->length; i++) {
michael@0 592 JSObject *obj = objects->vector[i];
michael@0 593 if (obj->is<JSFunction>()) {
michael@0 594 JSFunction *fun = &obj->as<JSFunction>();
michael@0 595 if (fun->hasScript()) {
michael@0 596 RootedScript nested(cx, fun->nonLazyScript());
michael@0 597 CallNewScriptHookForAllScripts(cx, nested);
michael@0 598 }
michael@0 599 }
michael@0 600 }
michael@0 601 }
michael@0 602
michael@0 603 // The global new script hook is called on every script that was compiled.
michael@0 604 RootedFunction function(cx, script->functionNonDelazifying());
michael@0 605 CallNewScriptHook(cx, script, function);
michael@0 606 }
michael@0 607
michael@0 608 static void
michael@0 609 LeaveParseTaskZone(JSRuntime *rt, ParseTask *task)
michael@0 610 {
michael@0 611 // Mark the zone as no longer in use by an ExclusiveContext, and available
michael@0 612 // to be collected by the GC.
michael@0 613 rt->clearUsedByExclusiveThread(task->cx->zone());
michael@0 614 }
michael@0 615
michael@0 616 JSScript *
michael@0 617 GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
michael@0 618 {
michael@0 619 ScopedJSDeletePtr<ParseTask> parseTask;
michael@0 620
michael@0 621 // The token is a ParseTask* which should be in the finished list.
michael@0 622 // Find and remove its entry.
michael@0 623 {
michael@0 624 AutoLockWorkerThreadState lock;
michael@0 625 ParseTaskVector &finished = parseFinishedList();
michael@0 626 for (size_t i = 0; i < finished.length(); i++) {
michael@0 627 if (finished[i] == token) {
michael@0 628 parseTask = finished[i];
michael@0 629 remove(finished, &i);
michael@0 630 break;
michael@0 631 }
michael@0 632 }
michael@0 633 }
michael@0 634 JS_ASSERT(parseTask);
michael@0 635
michael@0 636 if (!maybecx) {
michael@0 637 LeaveParseTaskZone(rt, parseTask);
michael@0 638 return nullptr;
michael@0 639 }
michael@0 640
michael@0 641 JSContext *cx = maybecx;
michael@0 642 JS_ASSERT(cx->compartment());
michael@0 643
michael@0 644 // Make sure we have all the constructors we need for the prototype
michael@0 645 // remapping below, since we can't GC while that's happening.
michael@0 646 Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
michael@0 647 if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object) ||
michael@0 648 !GlobalObject::ensureConstructor(cx, global, JSProto_Array) ||
michael@0 649 !GlobalObject::ensureConstructor(cx, global, JSProto_Function) ||
michael@0 650 !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) ||
michael@0 651 !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator))
michael@0 652 {
michael@0 653 LeaveParseTaskZone(rt, parseTask);
michael@0 654 return nullptr;
michael@0 655 }
michael@0 656
michael@0 657 LeaveParseTaskZone(rt, parseTask);
michael@0 658
michael@0 659 // Point the prototypes of any objects in the script's compartment to refer
michael@0 660 // to the corresponding prototype in the new compartment. This will briefly
michael@0 661 // create cross compartment pointers, which will be fixed by the
michael@0 662 // MergeCompartments call below.
michael@0 663 for (gc::CellIter iter(parseTask->cx->zone(), gc::FINALIZE_TYPE_OBJECT);
michael@0 664 !iter.done();
michael@0 665 iter.next())
michael@0 666 {
michael@0 667 types::TypeObject *object = iter.get<types::TypeObject>();
michael@0 668 TaggedProto proto(object->proto());
michael@0 669 if (!proto.isObject())
michael@0 670 continue;
michael@0 671
michael@0 672 JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
michael@0 673 if (key == JSProto_Null)
michael@0 674 continue;
michael@0 675 JS_ASSERT(key == JSProto_Object || key == JSProto_Array ||
michael@0 676 key == JSProto_Function || key == JSProto_RegExp ||
michael@0 677 key == JSProto_Iterator);
michael@0 678
michael@0 679 JSObject *newProto = GetBuiltinPrototypePure(global, key);
michael@0 680 JS_ASSERT(newProto);
michael@0 681
michael@0 682 object->setProtoUnchecked(newProto);
michael@0 683 }
michael@0 684
michael@0 685 // Move the parsed script and all its contents into the desired compartment.
michael@0 686 gc::MergeCompartments(parseTask->cx->compartment(), cx->compartment());
michael@0 687 parseTask->finish();
michael@0 688
michael@0 689 RootedScript script(rt, parseTask->script);
michael@0 690 assertSameCompartment(cx, script);
michael@0 691
michael@0 692 // Report any error or warnings generated during the parse, and inform the
michael@0 693 // debugger about the compiled scripts.
michael@0 694 for (size_t i = 0; i < parseTask->errors.length(); i++)
michael@0 695 parseTask->errors[i]->throwError(cx);
michael@0 696 if (parseTask->overRecursed)
michael@0 697 js_ReportOverRecursed(cx);
michael@0 698
michael@0 699 if (script) {
michael@0 700 // The Debugger only needs to be told about the topmost script that was compiled.
michael@0 701 GlobalObject *compileAndGoGlobal = nullptr;
michael@0 702 if (script->compileAndGo())
michael@0 703 compileAndGoGlobal = &script->global();
michael@0 704 Debugger::onNewScript(cx, script, compileAndGoGlobal);
michael@0 705
michael@0 706 // The NewScript hook needs to be called for all compiled scripts.
michael@0 707 CallNewScriptHookForAllScripts(cx, script);
michael@0 708 }
michael@0 709
michael@0 710 return script;
michael@0 711 }
michael@0 712
michael@0 713 void
michael@0 714 WorkerThread::destroy()
michael@0 715 {
michael@0 716 if (thread) {
michael@0 717 {
michael@0 718 AutoLockWorkerThreadState lock;
michael@0 719 terminate = true;
michael@0 720
michael@0 721 /* Notify all workers, to ensure that this thread wakes up. */
michael@0 722 WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
michael@0 723 }
michael@0 724
michael@0 725 PR_JoinThread(thread);
michael@0 726 }
michael@0 727
michael@0 728 if (!threadData.empty())
michael@0 729 threadData.destroy();
michael@0 730 }
michael@0 731
michael@0 732 /* static */
michael@0 733 void
michael@0 734 WorkerThread::ThreadMain(void *arg)
michael@0 735 {
michael@0 736 PR_SetCurrentThreadName("Analysis Helper");
michael@0 737 static_cast<WorkerThread *>(arg)->threadLoop();
michael@0 738 }
michael@0 739
michael@0 740 void
michael@0 741 WorkerThread::handleAsmJSWorkload()
michael@0 742 {
michael@0 743 #ifdef JS_ION
michael@0 744 JS_ASSERT(WorkerThreadState().isLocked());
michael@0 745 JS_ASSERT(WorkerThreadState().canStartAsmJSCompile());
michael@0 746 JS_ASSERT(idle());
michael@0 747
michael@0 748 asmData = WorkerThreadState().asmJSWorklist().popCopy();
michael@0 749 bool success = false;
michael@0 750
michael@0 751 do {
michael@0 752 AutoUnlockWorkerThreadState unlock;
michael@0 753 PerThreadData::AutoEnterRuntime enter(threadData.addr(), asmData->runtime);
michael@0 754
michael@0 755 jit::IonContext icx(asmData->mir->compartment->runtime(),
michael@0 756 asmData->mir->compartment,
michael@0 757 &asmData->mir->alloc());
michael@0 758
michael@0 759 int64_t before = PRMJ_Now();
michael@0 760
michael@0 761 if (!OptimizeMIR(asmData->mir))
michael@0 762 break;
michael@0 763
michael@0 764 asmData->lir = GenerateLIR(asmData->mir);
michael@0 765 if (!asmData->lir)
michael@0 766 break;
michael@0 767
michael@0 768 int64_t after = PRMJ_Now();
michael@0 769 asmData->compileTime = (after - before) / PRMJ_USEC_PER_MSEC;
michael@0 770
michael@0 771 success = true;
michael@0 772 } while(0);
michael@0 773
michael@0 774 // On failure, signal parent for harvesting in CancelOutstandingJobs().
michael@0 775 if (!success) {
michael@0 776 WorkerThreadState().noteAsmJSFailure(asmData->func);
michael@0 777 WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
michael@0 778 asmData = nullptr;
michael@0 779 return;
michael@0 780 }
michael@0 781
michael@0 782 // On success, move work to the finished list.
michael@0 783 WorkerThreadState().asmJSFinishedList().append(asmData);
michael@0 784 asmData = nullptr;
michael@0 785
michael@0 786 // Notify the main thread in case it's blocked waiting for a LifoAlloc.
michael@0 787 WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
michael@0 788 #else
michael@0 789 MOZ_CRASH();
michael@0 790 #endif // JS_ION
michael@0 791 }
michael@0 792
michael@0 793 void
michael@0 794 WorkerThread::handleIonWorkload()
michael@0 795 {
michael@0 796 #ifdef JS_ION
michael@0 797 JS_ASSERT(WorkerThreadState().isLocked());
michael@0 798 JS_ASSERT(WorkerThreadState().canStartIonCompile());
michael@0 799 JS_ASSERT(idle());
michael@0 800
michael@0 801 ionBuilder = WorkerThreadState().ionWorklist().popCopy();
michael@0 802
michael@0 803 TraceLogger *logger = TraceLoggerForCurrentThread();
michael@0 804 AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, ionBuilder->script()));
michael@0 805 AutoTraceLog logCompile(logger, TraceLogger::IonCompilation);
michael@0 806
michael@0 807 JSRuntime *rt = ionBuilder->script()->compartment()->runtimeFromAnyThread();
michael@0 808
michael@0 809 {
michael@0 810 AutoUnlockWorkerThreadState unlock;
michael@0 811 PerThreadData::AutoEnterRuntime enter(threadData.addr(),
michael@0 812 ionBuilder->script()->runtimeFromAnyThread());
michael@0 813 jit::IonContext ictx(jit::CompileRuntime::get(rt),
michael@0 814 jit::CompileCompartment::get(ionBuilder->script()->compartment()),
michael@0 815 &ionBuilder->alloc());
michael@0 816 ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
michael@0 817 }
michael@0 818
michael@0 819 FinishOffThreadIonCompile(ionBuilder);
michael@0 820 ionBuilder = nullptr;
michael@0 821
michael@0 822 // Ping the main thread so that the compiled code can be incorporated
michael@0 823 // at the next interrupt callback. Don't interrupt Ion code for this, as
michael@0 824 // this incorporation can be delayed indefinitely without affecting
michael@0 825 // performance as long as the main thread is actually executing Ion code.
michael@0 826 rt->requestInterrupt(JSRuntime::RequestInterruptAnyThreadDontStopIon);
michael@0 827
michael@0 828 // Notify the main thread in case it is waiting for the compilation to finish.
michael@0 829 WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
michael@0 830 #else
michael@0 831 MOZ_CRASH();
michael@0 832 #endif // JS_ION
michael@0 833 }
michael@0 834
michael@0 835 void
michael@0 836 ExclusiveContext::setWorkerThread(WorkerThread *workerThread)
michael@0 837 {
michael@0 838 workerThread_ = workerThread;
michael@0 839 perThreadData = workerThread->threadData.addr();
michael@0 840 }
michael@0 841
michael@0 842 frontend::CompileError &
michael@0 843 ExclusiveContext::addPendingCompileError()
michael@0 844 {
michael@0 845 frontend::CompileError *error = js_new<frontend::CompileError>();
michael@0 846 if (!error)
michael@0 847 MOZ_CRASH();
michael@0 848 if (!workerThread()->parseTask->errors.append(error))
michael@0 849 MOZ_CRASH();
michael@0 850 return *error;
michael@0 851 }
michael@0 852
michael@0 853 void
michael@0 854 ExclusiveContext::addPendingOverRecursed()
michael@0 855 {
michael@0 856 if (workerThread()->parseTask)
michael@0 857 workerThread()->parseTask->overRecursed = true;
michael@0 858 }
michael@0 859
michael@0 860 void
michael@0 861 WorkerThread::handleParseWorkload()
michael@0 862 {
michael@0 863 JS_ASSERT(WorkerThreadState().isLocked());
michael@0 864 JS_ASSERT(WorkerThreadState().canStartParseTask());
michael@0 865 JS_ASSERT(idle());
michael@0 866
michael@0 867 parseTask = WorkerThreadState().parseWorklist().popCopy();
michael@0 868 parseTask->cx->setWorkerThread(this);
michael@0 869
michael@0 870 {
michael@0 871 AutoUnlockWorkerThreadState unlock;
michael@0 872 PerThreadData::AutoEnterRuntime enter(threadData.addr(),
michael@0 873 parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
michael@0 874 SourceBufferHolder srcBuf(parseTask->chars, parseTask->length,
michael@0 875 SourceBufferHolder::NoOwnership);
michael@0 876 parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
michael@0 877 NullPtr(), NullPtr(),
michael@0 878 parseTask->options,
michael@0 879 srcBuf);
michael@0 880 }
michael@0 881
michael@0 882 // The callback is invoked while we are still off the main thread.
michael@0 883 parseTask->callback(parseTask, parseTask->callbackData);
michael@0 884
michael@0 885 // FinishOffThreadScript will need to be called on the script to
michael@0 886 // migrate it into the correct compartment.
michael@0 887 WorkerThreadState().parseFinishedList().append(parseTask);
michael@0 888
michael@0 889 parseTask = nullptr;
michael@0 890
michael@0 891 // Notify the main thread in case it is waiting for the parse/emit to finish.
michael@0 892 WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
michael@0 893 }
michael@0 894
michael@0 895 void
michael@0 896 WorkerThread::handleCompressionWorkload()
michael@0 897 {
michael@0 898 JS_ASSERT(WorkerThreadState().isLocked());
michael@0 899 JS_ASSERT(WorkerThreadState().canStartCompressionTask());
michael@0 900 JS_ASSERT(idle());
michael@0 901
michael@0 902 compressionTask = WorkerThreadState().compressionWorklist().popCopy();
michael@0 903 compressionTask->workerThread = this;
michael@0 904
michael@0 905 {
michael@0 906 AutoUnlockWorkerThreadState unlock;
michael@0 907 if (!compressionTask->work())
michael@0 908 compressionTask->setOOM();
michael@0 909 }
michael@0 910
michael@0 911 compressionTask->workerThread = nullptr;
michael@0 912 compressionTask = nullptr;
michael@0 913
michael@0 914 // Notify the main thread in case it is waiting for the compression to finish.
michael@0 915 WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
michael@0 916 }
michael@0 917
michael@0 918 bool
michael@0 919 js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
michael@0 920 {
michael@0 921 EnsureWorkerThreadsInitialized(cx);
michael@0 922
michael@0 923 AutoLockWorkerThreadState lock;
michael@0 924
michael@0 925 if (!WorkerThreadState().compressionWorklist().append(task))
michael@0 926 return false;
michael@0 927
michael@0 928 WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
michael@0 929 return true;
michael@0 930 }
michael@0 931
michael@0 932 bool
michael@0 933 GlobalWorkerThreadState::compressionInProgress(SourceCompressionTask *task)
michael@0 934 {
michael@0 935 JS_ASSERT(isLocked());
michael@0 936 for (size_t i = 0; i < compressionWorklist().length(); i++) {
michael@0 937 if (compressionWorklist()[i] == task)
michael@0 938 return true;
michael@0 939 }
michael@0 940 for (size_t i = 0; i < threadCount; i++) {
michael@0 941 if (threads[i].compressionTask == task)
michael@0 942 return true;
michael@0 943 }
michael@0 944 return false;
michael@0 945 }
michael@0 946
michael@0 947 bool
michael@0 948 SourceCompressionTask::complete()
michael@0 949 {
michael@0 950 JS_ASSERT_IF(!ss, !chars);
michael@0 951 if (active()) {
michael@0 952 AutoLockWorkerThreadState lock;
michael@0 953
michael@0 954 while (WorkerThreadState().compressionInProgress(this))
michael@0 955 WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
michael@0 956
michael@0 957 ss->ready_ = true;
michael@0 958
michael@0 959 // Update memory accounting.
michael@0 960 if (!oom)
michael@0 961 cx->updateMallocCounter(ss->computedSizeOfData());
michael@0 962
michael@0 963 ss = nullptr;
michael@0 964 chars = nullptr;
michael@0 965 }
michael@0 966 if (oom) {
michael@0 967 js_ReportOutOfMemory(cx);
michael@0 968 return false;
michael@0 969 }
michael@0 970 return true;
michael@0 971 }
michael@0 972
michael@0 973 SourceCompressionTask *
michael@0 974 GlobalWorkerThreadState::compressionTaskForSource(ScriptSource *ss)
michael@0 975 {
michael@0 976 JS_ASSERT(isLocked());
michael@0 977 for (size_t i = 0; i < compressionWorklist().length(); i++) {
michael@0 978 SourceCompressionTask *task = compressionWorklist()[i];
michael@0 979 if (task->source() == ss)
michael@0 980 return task;
michael@0 981 }
michael@0 982 for (size_t i = 0; i < threadCount; i++) {
michael@0 983 SourceCompressionTask *task = threads[i].compressionTask;
michael@0 984 if (task && task->source() == ss)
michael@0 985 return task;
michael@0 986 }
michael@0 987 return nullptr;
michael@0 988 }
michael@0 989
michael@0 990 const jschar *
michael@0 991 ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
michael@0 992 {
michael@0 993 // If this is being compressed off thread, return its uncompressed chars.
michael@0 994
michael@0 995 if (ready()) {
michael@0 996 // Compression has already finished on the source.
michael@0 997 return nullptr;
michael@0 998 }
michael@0 999
michael@0 1000 AutoLockWorkerThreadState lock;
michael@0 1001
michael@0 1002 // Look for a token that hasn't finished compressing and whose source is
michael@0 1003 // the given ScriptSource.
michael@0 1004 if (SourceCompressionTask *task = WorkerThreadState().compressionTaskForSource(this))
michael@0 1005 return task->uncompressedChars();
michael@0 1006
michael@0 1007 // Compressing has finished, so this ScriptSource is ready. Avoid future
michael@0 1008 // queries on the worker thread state when getting the chars.
michael@0 1009 ready_ = true;
michael@0 1010
michael@0 1011 return nullptr;
michael@0 1012 }
michael@0 1013
michael@0 1014 void
michael@0 1015 WorkerThread::threadLoop()
michael@0 1016 {
michael@0 1017 JS::AutoAssertNoGC nogc;
michael@0 1018 AutoLockWorkerThreadState lock;
michael@0 1019
michael@0 1020 js::TlsPerThreadData.set(threadData.addr());
michael@0 1021
michael@0 1022 // Compute the thread's stack limit, for over-recursed checks.
michael@0 1023 uintptr_t stackLimit = GetNativeStackBase();
michael@0 1024 #if JS_STACK_GROWTH_DIRECTION > 0
michael@0 1025 stackLimit += WORKER_STACK_QUOTA;
michael@0 1026 #else
michael@0 1027 stackLimit -= WORKER_STACK_QUOTA;
michael@0 1028 #endif
michael@0 1029 for (size_t i = 0; i < ArrayLength(threadData.ref().nativeStackLimit); i++)
michael@0 1030 threadData.ref().nativeStackLimit[i] = stackLimit;
michael@0 1031
michael@0 1032 while (true) {
michael@0 1033 JS_ASSERT(!ionBuilder && !asmData);
michael@0 1034
michael@0 1035 // Block until a task is available.
michael@0 1036 while (true) {
michael@0 1037 if (terminate)
michael@0 1038 return;
michael@0 1039 if (WorkerThreadState().canStartIonCompile() ||
michael@0 1040 WorkerThreadState().canStartAsmJSCompile() ||
michael@0 1041 WorkerThreadState().canStartParseTask() ||
michael@0 1042 WorkerThreadState().canStartCompressionTask())
michael@0 1043 {
michael@0 1044 break;
michael@0 1045 }
michael@0 1046 WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
michael@0 1047 }
michael@0 1048
michael@0 1049 // Dispatch tasks, prioritizing AsmJS work.
michael@0 1050 if (WorkerThreadState().canStartAsmJSCompile())
michael@0 1051 handleAsmJSWorkload();
michael@0 1052 else if (WorkerThreadState().canStartIonCompile())
michael@0 1053 handleIonWorkload();
michael@0 1054 else if (WorkerThreadState().canStartParseTask())
michael@0 1055 handleParseWorkload();
michael@0 1056 else if (WorkerThreadState().canStartCompressionTask())
michael@0 1057 handleCompressionWorkload();
michael@0 1058 else
michael@0 1059 MOZ_ASSUME_UNREACHABLE("No task to perform");
michael@0 1060 }
michael@0 1061 }
michael@0 1062
michael@0 1063 #else /* JS_THREADSAFE */
michael@0 1064
michael@0 1065 using namespace js;
michael@0 1066
michael@0 1067 #ifdef JS_ION
michael@0 1068
michael@0 1069 bool
michael@0 1070 js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
michael@0 1071 {
michael@0 1072 MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
michael@0 1073 }
michael@0 1074
michael@0 1075 bool
michael@0 1076 js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
michael@0 1077 {
michael@0 1078 MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
michael@0 1079 }
michael@0 1080
michael@0 1081 #endif // JS_ION
michael@0 1082
michael@0 1083 void
michael@0 1084 js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
michael@0 1085 {
michael@0 1086 }
michael@0 1087
michael@0 1088 void
michael@0 1089 js::CancelOffThreadParses(JSRuntime *rt)
michael@0 1090 {
michael@0 1091 }
michael@0 1092
michael@0 1093 bool
michael@0 1094 js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
michael@0 1095 const jschar *chars, size_t length,
michael@0 1096 JS::OffThreadCompileCallback callback, void *callbackData)
michael@0 1097 {
michael@0 1098 MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
michael@0 1099 }
michael@0 1100
michael@0 1101 bool
michael@0 1102 js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
michael@0 1103 {
michael@0 1104 MOZ_ASSUME_UNREACHABLE("Off thread compression not available");
michael@0 1105 }
michael@0 1106
michael@0 1107 bool
michael@0 1108 SourceCompressionTask::complete()
michael@0 1109 {
michael@0 1110 JS_ASSERT(!active() && !oom);
michael@0 1111 return true;
michael@0 1112 }
michael@0 1113
michael@0 1114 const jschar *
michael@0 1115 ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
michael@0 1116 {
michael@0 1117 JS_ASSERT(ready());
michael@0 1118 return nullptr;
michael@0 1119 }
michael@0 1120
michael@0 1121 frontend::CompileError &
michael@0 1122 ExclusiveContext::addPendingCompileError()
michael@0 1123 {
michael@0 1124 MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
michael@0 1125 }
michael@0 1126
michael@0 1127 void
michael@0 1128 ExclusiveContext::addPendingOverRecursed()
michael@0 1129 {
michael@0 1130 MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
michael@0 1131 }
michael@0 1132
michael@0 1133 #endif /* JS_THREADSAFE */

mercurial