LCOV - code coverage report
Current view: top level - js/src/vm - HelperThreads.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 579 1076 53.8 %
Date: 2017-07-14 16:53:18 Functions: 91 155 58.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/HelperThreads.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/Maybe.h"
      11             : #include "mozilla/Unused.h"
      12             : 
      13             : #include "jsnativestack.h"
      14             : 
      15             : #include "builtin/Promise.h"
      16             : #include "frontend/BytecodeCompiler.h"
      17             : #include "gc/GCInternals.h"
      18             : #include "jit/IonBuilder.h"
      19             : #include "threading/CpuCount.h"
      20             : #include "vm/Debugger.h"
      21             : #include "vm/ErrorReporting.h"
      22             : #include "vm/SharedImmutableStringsCache.h"
      23             : #include "vm/Time.h"
      24             : #include "vm/TraceLogging.h"
      25             : #include "vm/Xdr.h"
      26             : 
      27             : #include "jscntxtinlines.h"
      28             : #include "jscompartmentinlines.h"
      29             : #include "jsobjinlines.h"
      30             : #include "jsscriptinlines.h"
      31             : 
      32             : #include "vm/NativeObject-inl.h"
      33             : 
      34             : using namespace js;
      35             : 
      36             : using mozilla::ArrayLength;
      37             : using mozilla::DebugOnly;
      38             : using mozilla::Unused;
      39             : using mozilla::TimeDuration;
      40             : 
      41             : namespace js {
      42             : 
      43             : GlobalHelperThreadState* gHelperThreadState = nullptr;
      44             : 
      45             : } // namespace js
      46             : 
      47             : bool
      48           3 : js::CreateHelperThreadsState()
      49             : {
      50           3 :     MOZ_ASSERT(!gHelperThreadState);
      51           3 :     gHelperThreadState = js_new<GlobalHelperThreadState>();
      52           3 :     return gHelperThreadState != nullptr;
      53             : }
      54             : 
      55             : void
      56           0 : js::DestroyHelperThreadsState()
      57             : {
      58           0 :     MOZ_ASSERT(gHelperThreadState);
      59           0 :     gHelperThreadState->finish();
      60           0 :     js_delete(gHelperThreadState);
      61           0 :     gHelperThreadState = nullptr;
      62           0 : }
      63             : 
      64             : bool
      65           4 : js::EnsureHelperThreadsInitialized()
      66             : {
      67           4 :     MOZ_ASSERT(gHelperThreadState);
      68           4 :     return gHelperThreadState->ensureInitialized();
      69             : }
      70             : 
      71             : static size_t
      72           3 : ThreadCountForCPUCount(size_t cpuCount)
      73             : {
      74             :     // Create additional threads on top of the number of cores available, to
      75             :     // provide some excess capacity in case threads pause each other.
      76             :     static const uint32_t EXCESS_THREADS = 4;
      77           3 :     return cpuCount + EXCESS_THREADS;
      78             : }
      79             : 
      80             : void
      81           0 : js::SetFakeCPUCount(size_t count)
      82             : {
      83             :     // This must be called before the threads have been initialized.
      84           0 :     MOZ_ASSERT(!HelperThreadState().threads);
      85             : 
      86           0 :     HelperThreadState().cpuCount = count;
      87           0 :     HelperThreadState().threadCount = ThreadCountForCPUCount(count);
      88           0 : }
      89             : 
      90             : bool
      91           0 : js::StartOffThreadWasmCompile(wasm::CompileTask* task)
      92             : {
      93           0 :     AutoLockHelperThreadState lock;
      94             : 
      95           0 :     if (!HelperThreadState().wasmWorklist(lock).append(task))
      96           0 :         return false;
      97             : 
      98           0 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
      99           0 :     return true;
     100             : }
     101             : 
     102             : bool
     103           8 : js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder)
     104             : {
     105          16 :     AutoLockHelperThreadState lock;
     106             : 
     107           8 :     if (!HelperThreadState().ionWorklist(lock).append(builder))
     108           0 :         return false;
     109             : 
     110           8 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     111           8 :     return true;
     112             : }
     113             : 
     114             : bool
     115           8 : js::StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
     116             : {
     117           8 :     MOZ_ASSERT(CanUseExtraThreads());
     118             : 
     119           8 :     if (!HelperThreadState().ionFreeList(lock).append(builder))
     120           0 :         return false;
     121             : 
     122           8 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     123           8 :     return true;
     124             : }
     125             : 
     126             : /*
     127             :  * Move an IonBuilder for which compilation has either finished, failed, or
     128             :  * been cancelled into the global finished compilation list. All off thread
     129             :  * compilations which are started must eventually be finished.
     130             :  */
     131             : static void
     132           8 : FinishOffThreadIonCompile(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
     133             : {
     134          16 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     135           8 :     if (!HelperThreadState().ionFinishedList(lock).append(builder))
     136           0 :         oomUnsafe.crash("FinishOffThreadIonCompile");
     137           8 :     builder->script()->zoneFromAnyThread()->group()->numFinishedBuilders++;
     138           8 : }
     139             : 
     140             : static JSRuntime*
     141          11 : GetSelectorRuntime(const CompilationSelector& selector)
     142             : {
     143             :     struct Matcher
     144             :     {
     145           6 :         JSRuntime* match(JSScript* script)    { return script->runtimeFromActiveCooperatingThread(); }
     146           0 :         JSRuntime* match(JSCompartment* comp) { return comp->runtimeFromActiveCooperatingThread(); }
     147           1 :         JSRuntime* match(ZonesInState zbs)    { return zbs.runtime; }
     148           4 :         JSRuntime* match(JSRuntime* runtime)  { return runtime; }
     149           0 :         JSRuntime* match(AllCompilations all) { return nullptr; }
     150             :     };
     151             : 
     152          11 :     return selector.match(Matcher());
     153             : }
     154             : 
     155             : static bool
     156          11 : JitDataStructuresExist(const CompilationSelector& selector)
     157             : {
     158             :     struct Matcher
     159             :     {
     160           6 :         bool match(JSScript* script)    { return !!script->compartment()->jitCompartment(); }
     161           0 :         bool match(JSCompartment* comp) { return !!comp->jitCompartment(); }
     162           1 :         bool match(ZonesInState zbs)    { return zbs.runtime->hasJitRuntime(); }
     163           4 :         bool match(JSRuntime* runtime)  { return runtime->hasJitRuntime(); }
     164           0 :         bool match(AllCompilations all) { return true; }
     165             :     };
     166             : 
     167          11 :     return selector.match(Matcher());
     168             : }
     169             : 
     170             : static bool
     171           1 : CompiledScriptMatches(const CompilationSelector& selector, JSScript* target)
     172             : {
     173             :     struct ScriptMatches
     174             :     {
     175             :         JSScript* target_;
     176             : 
     177           0 :         bool match(JSScript* script)    { return script == target_; }
     178           0 :         bool match(JSCompartment* comp) { return comp == target_->compartment(); }
     179           1 :         bool match(JSRuntime* runtime)  { return runtime == target_->runtimeFromAnyThread(); }
     180           0 :         bool match(AllCompilations all) { return true; }
     181           0 :         bool match(ZonesInState zbs)    {
     182           0 :             return zbs.runtime == target_->runtimeFromAnyThread() &&
     183           0 :                    zbs.state == target_->zoneFromAnyThread()->gcState();
     184             :         }
     185             :     };
     186             : 
     187           1 :     return selector.match(ScriptMatches{target});
     188             : }
     189             : 
     190             : void
     191          11 : js::CancelOffThreadIonCompile(const CompilationSelector& selector, bool discardLazyLinkList)
     192             : {
     193          11 :     if (!JitDataStructuresExist(selector))
     194           0 :         return;
     195             : 
     196          22 :     AutoLockHelperThreadState lock;
     197             : 
     198          11 :     if (!HelperThreadState().threads)
     199           0 :         return;
     200             : 
     201             :     /* Cancel any pending entries for which processing hasn't started. */
     202          11 :     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     203          11 :     for (size_t i = 0; i < worklist.length(); i++) {
     204           0 :         jit::IonBuilder* builder = worklist[i];
     205           0 :         if (CompiledScriptMatches(selector, builder->script())) {
     206           0 :             FinishOffThreadIonCompile(builder, lock);
     207           0 :             HelperThreadState().remove(worklist, &i);
     208             :         }
     209             :     }
     210             : 
     211             :     /* Wait for in progress entries to finish up. */
     212             :     bool cancelled;
     213          11 :     do {
     214          11 :         cancelled = false;
     215          11 :         bool unpaused = false;
     216         143 :         for (auto& helper : *HelperThreadState().threads) {
     217         132 :             if (helper.ionBuilder() &&
     218           0 :                 CompiledScriptMatches(selector, helper.ionBuilder()->script()))
     219             :             {
     220           0 :                 helper.ionBuilder()->cancel();
     221           0 :                 if (helper.pause) {
     222           0 :                     helper.pause = false;
     223           0 :                     unpaused = true;
     224             :                 }
     225           0 :                 cancelled = true;
     226             :             }
     227             :         }
     228          11 :         if (unpaused)
     229           0 :             HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE, lock);
     230          11 :         if (cancelled)
     231           0 :             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     232             :     } while (cancelled);
     233             : 
     234             :     /* Cancel code generation for any completed entries. */
     235          11 :     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     236          11 :     for (size_t i = 0; i < finished.length(); i++) {
     237           0 :         jit::IonBuilder* builder = finished[i];
     238           0 :         if (CompiledScriptMatches(selector, builder->script())) {
     239           0 :             builder->script()->zone()->group()->numFinishedBuilders--;
     240           0 :             jit::FinishOffThreadBuilder(nullptr, builder, lock);
     241           0 :             HelperThreadState().remove(finished, &i);
     242             :         }
     243             :     }
     244             : 
     245             :     /* Cancel lazy linking for pending builders (attached to the ionScript). */
     246          11 :     if (discardLazyLinkList) {
     247          11 :         MOZ_ASSERT(!selector.is<AllCompilations>());
     248          11 :         JSRuntime* runtime = GetSelectorRuntime(selector);
     249         116 :         for (ZoneGroupsIter group(runtime); !group.done(); group.next()) {
     250         105 :             jit::IonBuilder* builder = group->ionLazyLinkList().getFirst();
     251         107 :             while (builder) {
     252           1 :                 jit::IonBuilder* next = builder->getNext();
     253           1 :                 if (CompiledScriptMatches(selector, builder->script()))
     254           1 :                     jit::FinishOffThreadBuilder(runtime, builder, lock);
     255           1 :                 builder = next;
     256             :             }
     257             :         }
     258             :     }
     259             : }
     260             : 
     261             : #ifdef DEBUG
     262             : bool
     263         210 : js::HasOffThreadIonCompile(JSCompartment* comp)
     264             : {
     265         420 :     AutoLockHelperThreadState lock;
     266             : 
     267         210 :     if (!HelperThreadState().threads || comp->isAtomsCompartment())
     268           1 :         return false;
     269             : 
     270         209 :     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     271         209 :     for (size_t i = 0; i < worklist.length(); i++) {
     272           0 :         jit::IonBuilder* builder = worklist[i];
     273           0 :         if (builder->script()->compartment() == comp)
     274           0 :             return true;
     275             :     }
     276             : 
     277        2717 :     for (auto& helper : *HelperThreadState().threads) {
     278        2508 :         if (helper.ionBuilder() && helper.ionBuilder()->script()->compartment() == comp)
     279           0 :             return true;
     280             :     }
     281             : 
     282         209 :     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     283         209 :     for (size_t i = 0; i < finished.length(); i++) {
     284           0 :         jit::IonBuilder* builder = finished[i];
     285           0 :         if (builder->script()->compartment() == comp)
     286           0 :             return true;
     287             :     }
     288             : 
     289         209 :     jit::IonBuilder* builder = comp->zone()->group()->ionLazyLinkList().getFirst();
     290         209 :     while (builder) {
     291           0 :         if (builder->script()->compartment() == comp)
     292           0 :             return true;
     293           0 :         builder = builder->getNext();
     294             :     }
     295             : 
     296         209 :     return false;
     297             : }
     298             : #endif
     299             : 
     300             : static const JSClassOps parseTaskGlobalClassOps = {
     301             :     nullptr, nullptr, nullptr, nullptr,
     302             :     nullptr, nullptr, nullptr, nullptr,
     303             :     nullptr, nullptr, nullptr, nullptr,
     304             :     JS_GlobalObjectTraceHook
     305             : };
     306             : 
     307             : static const JSClass parseTaskGlobalClass = {
     308             :     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     309             :     &parseTaskGlobalClassOps
     310             : };
     311             : 
     312           0 : ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
     313             :                      const char16_t* chars, size_t length,
     314           0 :                      JS::OffThreadCompileCallback callback, void* callbackData)
     315           0 :   : kind(kind), options(cx), data(AsVariant(TwoByteChars(chars, length))),
     316             :     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     317             :     parseGlobal(parseGlobal),
     318             :     callback(callback), callbackData(callbackData),
     319             :     scripts(cx), sourceObjects(cx),
     320           0 :     overRecursed(false), outOfMemory(false)
     321             : {
     322           0 :     MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
     323           0 :     MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
     324           0 : }
     325             : 
     326           0 : ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
     327             :                      const JS::TranscodeRange& range,
     328           0 :                      JS::OffThreadCompileCallback callback, void* callbackData)
     329           0 :   : kind(kind), options(cx), data(AsVariant(range)),
     330             :     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     331             :     parseGlobal(parseGlobal),
     332             :     callback(callback), callbackData(callbackData),
     333             :     scripts(cx), sourceObjects(cx),
     334           0 :     overRecursed(false), outOfMemory(false)
     335             : {
     336           0 :     MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
     337           0 :     MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
     338           0 : }
     339             : 
     340          16 : ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
     341             :                      JS::TranscodeSources& sources,
     342          16 :                      JS::OffThreadCompileCallback callback, void* callbackData)
     343          32 :   : kind(kind), options(cx), data(AsVariant(&sources)),
     344             :     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     345             :     parseGlobal(parseGlobal),
     346             :     callback(callback), callbackData(callbackData),
     347             :     scripts(cx), sourceObjects(cx),
     348          48 :     overRecursed(false), outOfMemory(false)
     349             : {
     350          16 :     MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
     351          16 :     MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
     352          16 : }
     353             : 
     354             : bool
     355          16 : ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
     356             : {
     357          16 :     if (!this->options.copy(cx, options))
     358           0 :         return false;
     359             : 
     360          16 :     return true;
     361             : }
     362             : 
     363             : void
     364          16 : ParseTask::activate(JSRuntime* rt)
     365             : {
     366          16 :     rt->setUsedByHelperThread(parseGlobal->zone());
     367          16 : }
     368             : 
     369             : bool
     370          15 : ParseTask::finish(JSContext* cx)
     371             : {
     372         191 :     for (auto& sourceObject : sourceObjects) {
     373         352 :         RootedScriptSource sso(cx, sourceObject);
     374         176 :         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
     375           0 :             return false;
     376         176 :         if (!sso->source()->tryCompressOffThread(cx))
     377           0 :             return false;
     378             :     }
     379             : 
     380          15 :     return true;
     381             : }
     382             : 
     383          30 : ParseTask::~ParseTask()
     384             : {
     385          15 :     for (size_t i = 0; i < errors.length(); i++)
     386           0 :         js_delete(errors[i]);
     387          15 : }
     388             : 
     389             : void
     390           5 : ParseTask::trace(JSTracer* trc)
     391             : {
     392           5 :     if (parseGlobal->runtimeFromAnyThread() != trc->runtime())
     393           0 :         return;
     394           5 :     Zone* zone = MaybeForwarded(parseGlobal)->zoneFromAnyThread();
     395           5 :     if (zone->usedByHelperThread()) {
     396           5 :         MOZ_ASSERT(!zone->isCollecting());
     397           5 :         return;
     398             :     }
     399             : 
     400           0 :     TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
     401           0 :     scripts.trace(trc);
     402           0 :     sourceObjects.trace(trc);
     403             : }
     404             : 
     405           0 : ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
     406             :                                  const char16_t* chars, size_t length,
     407           0 :                                  JS::OffThreadCompileCallback callback, void* callbackData)
     408             :   : ParseTask(ParseTaskKind::Script, cx, parseGlobal, chars, length, callback,
     409           0 :               callbackData)
     410             : {
     411           0 : }
     412             : 
     413             : void
     414           0 : ScriptParseTask::parse(JSContext* cx)
     415             : {
     416           0 :     auto& range = data.as<TwoByteChars>();
     417           0 :     SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
     418           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     419             : 
     420           0 :     JSScript* script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
     421             :                                                      options, srcBuf,
     422           0 :                                                      /* sourceObjectOut = */ &sourceObject.get());
     423           0 :     if (script)
     424           0 :         scripts.infallibleAppend(script);
     425           0 :     if (sourceObject)
     426           0 :         sourceObjects.infallibleAppend(sourceObject);
     427           0 : }
     428             : 
     429           0 : ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
     430             :                                  const char16_t* chars, size_t length,
     431           0 :                                  JS::OffThreadCompileCallback callback, void* callbackData)
     432             :   : ParseTask(ParseTaskKind::Module, cx, parseGlobal, chars, length, callback,
     433           0 :               callbackData)
     434             : {
     435           0 : }
     436             : 
     437             : void
     438           0 : ModuleParseTask::parse(JSContext* cx)
     439             : {
     440           0 :     auto& range = data.as<TwoByteChars>();
     441           0 :     SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
     442           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     443             : 
     444           0 :     ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject.get());
     445           0 :     if (module) {
     446           0 :         scripts.infallibleAppend(module->script());
     447           0 :         if (sourceObject)
     448           0 :             sourceObjects.infallibleAppend(sourceObject);
     449             :     }
     450           0 : }
     451             : 
     452           0 : ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
     453             :                                    const JS::TranscodeRange& range,
     454           0 :                                    JS::OffThreadCompileCallback callback, void* callbackData)
     455             :   : ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal,
     456           0 :               range, callback, callbackData)
     457             : {
     458           0 : }
     459             : 
     460             : void
     461           0 : ScriptDecodeTask::parse(JSContext* cx)
     462             : {
     463           0 :     RootedScript resultScript(cx);
     464           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     465             : 
     466           0 :     XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject.get(),
     467           0 :                                 data.as<const JS::TranscodeRange>());
     468           0 :     decoder.codeScript(&resultScript);
     469           0 :     MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
     470           0 :     if (decoder.resultCode() == JS::TranscodeResult_Ok) {
     471           0 :         scripts.infallibleAppend(resultScript);
     472           0 :         if (sourceObject)
     473           0 :             sourceObjects.infallibleAppend(sourceObject);
     474             :     }
     475           0 : }
     476             : 
     477          16 : MultiScriptsDecodeTask::MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
     478             :                                      JS::TranscodeSources& sources,
     479          16 :                                      JS::OffThreadCompileCallback callback, void* callbackData)
     480             :   : ParseTask(ParseTaskKind::MultiScriptsDecode, cx, parseGlobal,
     481          16 :               sources, callback, callbackData)
     482             : {
     483          16 : }
     484             : 
     485             : void
     486          16 : MultiScriptsDecodeTask::parse(JSContext* cx)
     487             : {
     488          16 :     auto sources = data.as<JS::TranscodeSources*>();
     489             : 
     490          32 :     if (!scripts.reserve(sources->length()) ||
     491          16 :         !sourceObjects.reserve(sources->length()))
     492             :     {
     493           0 :         return;
     494             :     }
     495             : 
     496         193 :     for (auto& source : *sources) {
     497         355 :         CompileOptions opts(cx, options);
     498         178 :         opts.setFileAndLine(source.filename, source.lineno);
     499             : 
     500         355 :         RootedScript resultScript(cx);
     501         355 :         Rooted<ScriptSourceObject*> sourceObject(cx);
     502             : 
     503         355 :         XDROffThreadDecoder decoder(cx, alloc, &opts, &sourceObject.get(), source.range);
     504         178 :         decoder.codeScript(&resultScript);
     505         177 :         MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
     506             : 
     507         177 :         if (decoder.resultCode() != JS::TranscodeResult_Ok)
     508           0 :             break;
     509         177 :         MOZ_ASSERT(resultScript);
     510         177 :         scripts.infallibleAppend(resultScript);
     511         177 :         sourceObjects.infallibleAppend(sourceObject);
     512             :     }
     513             : }
     514             : 
     515             : void
     516           0 : js::CancelOffThreadParses(JSRuntime* rt)
     517             : {
     518           0 :     AutoLockHelperThreadState lock;
     519             : 
     520           0 :     if (!HelperThreadState().threads)
     521           0 :         return;
     522             : 
     523             : #ifdef DEBUG
     524             :     GlobalHelperThreadState::ParseTaskVector& waitingOnGC =
     525           0 :         HelperThreadState().parseWaitingOnGC(lock);
     526           0 :     for (size_t i = 0; i < waitingOnGC.length(); i++)
     527           0 :         MOZ_ASSERT(!waitingOnGC[i]->runtimeMatches(rt));
     528             : #endif
     529             : 
     530             :     // Instead of forcibly canceling pending parse tasks, just wait for all scheduled
     531             :     // and in progress ones to complete. Otherwise the final GC may not collect
     532             :     // everything due to zones being used off thread.
     533             :     while (true) {
     534           0 :         bool pending = false;
     535           0 :         GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist(lock);
     536           0 :         for (size_t i = 0; i < worklist.length(); i++) {
     537           0 :             ParseTask* task = worklist[i];
     538           0 :             if (task->runtimeMatches(rt))
     539           0 :                 pending = true;
     540             :         }
     541           0 :         if (!pending) {
     542           0 :             bool inProgress = false;
     543           0 :             for (auto& thread : *HelperThreadState().threads) {
     544           0 :                 ParseTask* task = thread.parseTask();
     545           0 :                 if (task && task->runtimeMatches(rt))
     546           0 :                     inProgress = true;
     547             :             }
     548           0 :             if (!inProgress)
     549           0 :                 break;
     550             :         }
     551           0 :         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     552           0 :     }
     553             : 
     554             :     // Clean up any parse tasks which haven't been finished by the active thread.
     555           0 :     GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList(lock);
     556             :     while (true) {
     557           0 :         bool found = false;
     558           0 :         for (size_t i = 0; i < finished.length(); i++) {
     559           0 :             ParseTask* task = finished[i];
     560           0 :             if (task->runtimeMatches(rt)) {
     561           0 :                 found = true;
     562           0 :                 AutoUnlockHelperThreadState unlock(lock);
     563           0 :                 HelperThreadState().cancelParseTask(rt, task->kind, task);
     564             :             }
     565             :         }
     566           0 :         if (!found)
     567           0 :             break;
     568           0 :     }
     569             : }
     570             : 
     571             : bool
     572          51 : js::OffThreadParsingMustWaitForGC(JSRuntime* rt)
     573             : {
     574             :     // Off thread parsing can't occur during incremental collections on the
     575             :     // atoms compartment, to avoid triggering barriers. (Outside the atoms
     576             :     // compartment, the compilation will use a new zone that is never
     577             :     // collected.) If an atoms-zone GC is in progress, hold off on executing the
     578             :     // parse task until the atoms-zone GC completes (see
     579             :     // EnqueuePendingParseTasksAfterGC).
     580          51 :     return rt->activeGCInAtomsZone();
     581             : }
     582             : 
     583             : static bool
     584         188 : EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
     585             : {
     586         188 :     if (!GlobalObject::ensureConstructor(cx, global, key))
     587           0 :         return false;
     588             : 
     589         188 :     MOZ_ASSERT(global->getPrototype(key).toObject().isDelegate(),
     590             :                "standard class prototype wasn't a delegate from birth");
     591         188 :     return true;
     592             : }
     593             : 
     594             : // Initialize all classes potentially created during parsing for use in parser
     595             : // data structures, template objects, &c.
     596             : static bool
     597          47 : EnsureParserCreatedClasses(JSContext* cx, ParseTaskKind kind)
     598             : {
     599          47 :     Handle<GlobalObject*> global = cx->global();
     600             : 
     601          47 :     if (!EnsureConstructor(cx, global, JSProto_Function))
     602           0 :         return false; // needed by functions, also adds object literals' proto
     603             : 
     604          47 :     if (!EnsureConstructor(cx, global, JSProto_Array))
     605           0 :         return false; // needed by array literals
     606             : 
     607          47 :     if (!EnsureConstructor(cx, global, JSProto_RegExp))
     608           0 :         return false; // needed by regular expression literals
     609             : 
     610          47 :     if (!EnsureConstructor(cx, global, JSProto_Iterator))
     611           0 :         return false; // needed by ???
     612             : 
     613          47 :     if (!GlobalObject::initStarGenerators(cx, global))
     614           0 :         return false; // needed by function*() {} and generator comprehensions
     615             : 
     616          47 :     if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global))
     617           0 :         return false;
     618             : 
     619          47 :     return true;
     620             : }
     621             : 
     622             : static JSObject*
     623          16 : CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoSuppressGC& nogc)
     624             : {
     625          16 :     JSCompartment* currentCompartment = cx->compartment();
     626             : 
     627             :     JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
     628          16 :                                               currentCompartment->behaviors());
     629             : 
     630          16 :     auto& creationOptions = compartmentOptions.creationOptions();
     631             : 
     632          16 :     creationOptions.setInvisibleToDebugger(true)
     633          16 :                    .setMergeable(true)
     634          16 :                    .setNewZoneInNewZoneGroup();
     635             : 
     636             :     // Don't falsely inherit the host's global trace hook.
     637          16 :     creationOptions.setTrace(nullptr);
     638             : 
     639          16 :     JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
     640          16 :                                           JS::FireOnNewGlobalHook, compartmentOptions);
     641          16 :     if (!global)
     642           0 :         return nullptr;
     643             : 
     644          16 :     JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
     645             : 
     646             :     // Initialize all classes required for parsing while still on the active
     647             :     // thread, for both the target and the new global so that prototype
     648             :     // pointers can be changed infallibly after parsing finishes.
     649          16 :     if (!EnsureParserCreatedClasses(cx, kind))
     650           0 :         return nullptr;
     651             :     {
     652          32 :         AutoCompartment ac(cx, global);
     653          16 :         if (!EnsureParserCreatedClasses(cx, kind))
     654           0 :             return nullptr;
     655             :     }
     656             : 
     657          16 :     return global;
     658             : }
     659             : 
     660             : static bool
     661          16 : QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
     662             : {
     663          16 :     if (OffThreadParsingMustWaitForGC(cx->runtime())) {
     664           0 :         AutoLockHelperThreadState lock;
     665           0 :         if (!HelperThreadState().parseWaitingOnGC(lock).append(task)) {
     666           0 :             ReportOutOfMemory(cx);
     667           0 :             return false;
     668             :         }
     669             :     } else {
     670          32 :         AutoLockHelperThreadState lock;
     671          16 :         if (!HelperThreadState().parseWorklist(lock).append(task)) {
     672           0 :             ReportOutOfMemory(cx);
     673           0 :             return false;
     674             :         }
     675             : 
     676          16 :         task->activate(cx->runtime());
     677          16 :         HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     678             :     }
     679             : 
     680          16 :     return true;
     681             : }
     682             : 
     683             : template <typename TaskFunctor>
     684             : bool
     685          16 : StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
     686             :                         ParseTaskKind kind, TaskFunctor& taskFunctor)
     687             : {
     688             :     // Suppress GC so that calls below do not trigger a new incremental GC
     689             :     // which could require barriers on the atoms compartment.
     690          32 :     gc::AutoSuppressGC nogc(cx);
     691          32 :     gc::AutoAssertNoNurseryAlloc noNurseryAlloc;
     692          32 :     AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
     693             : 
     694          16 :     JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
     695          16 :     if (!global)
     696           0 :         return false;
     697             : 
     698          32 :     ScopedJSDeletePtr<ParseTask> task(taskFunctor(global));
     699          16 :     if (!task)
     700           0 :         return false;
     701             : 
     702          16 :     if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
     703           0 :         return false;
     704             : 
     705          16 :     task.forget();
     706             : 
     707          16 :     return true;
     708             : }
     709             : 
     710             : 
     711             : bool
     712           0 : js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
     713             :                               const char16_t* chars, size_t length,
     714             :                               JS::OffThreadCompileCallback callback, void* callbackData)
     715             : {
     716           0 :     auto functor = [&](JSObject* global) -> ScriptParseTask* {
     717           0 :         return cx->new_<ScriptParseTask>(cx, global, chars, length,
     718           0 :                                          callback, callbackData);
     719           0 :     };
     720           0 :     return StartOffThreadParseTask(cx, options, ParseTaskKind::Script, functor);
     721             : }
     722             : 
     723             : bool
     724           0 : js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
     725             :                               const char16_t* chars, size_t length,
     726             :                               JS::OffThreadCompileCallback callback, void* callbackData)
     727             : {
     728           0 :     auto functor = [&](JSObject* global) -> ModuleParseTask* {
     729           0 :         return cx->new_<ModuleParseTask>(cx, global, chars, length,
     730           0 :                                          callback, callbackData);
     731           0 :     };
     732           0 :     return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
     733             : }
     734             : 
     735             : bool
     736           0 : js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
     737             :                                const JS::TranscodeRange& range,
     738             :                                JS::OffThreadCompileCallback callback, void* callbackData)
     739             : {
     740           0 :     auto functor = [&](JSObject* global) -> ScriptDecodeTask* {
     741           0 :         return cx->new_<ScriptDecodeTask>(cx, global, range, callback, callbackData);
     742           0 :     };
     743           0 :     return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
     744             : }
     745             : 
     746             : bool
     747          16 : js::StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
     748             :                                      JS::TranscodeSources& sources,
     749             :                                      JS::OffThreadCompileCallback callback, void* callbackData)
     750             : {
     751          16 :     auto functor = [&](JSObject* global) -> MultiScriptsDecodeTask* {
     752          16 :         return cx->new_<MultiScriptsDecodeTask>(cx, global, sources, callback, callbackData);
     753          16 :     };
     754          16 :     return StartOffThreadParseTask(cx, options, ParseTaskKind::MultiScriptsDecode, functor);
     755             : }
     756             : 
     757             : void
     758           0 : js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
     759             : {
     760           0 :     MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
     761             : 
     762           0 :     GlobalHelperThreadState::ParseTaskVector newTasks;
     763             :     {
     764           0 :         AutoLockHelperThreadState lock;
     765             :         GlobalHelperThreadState::ParseTaskVector& waiting =
     766           0 :             HelperThreadState().parseWaitingOnGC(lock);
     767             : 
     768           0 :         for (size_t i = 0; i < waiting.length(); i++) {
     769           0 :             ParseTask* task = waiting[i];
     770           0 :             if (task->runtimeMatches(rt) && !task->parseGlobal->zone()->wasGCStarted()) {
     771           0 :                 AutoEnterOOMUnsafeRegion oomUnsafe;
     772           0 :                 if (!newTasks.append(task))
     773           0 :                     oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
     774           0 :                 HelperThreadState().remove(waiting, &i);
     775             :             }
     776             :         }
     777             :     }
     778             : 
     779           0 :     if (newTasks.empty())
     780           0 :         return;
     781             : 
     782             :     // This logic should mirror the contents of the !activeGCInAtomsZone()
     783             :     // branch in StartOffThreadParseScript:
     784             : 
     785           0 :     for (size_t i = 0; i < newTasks.length(); i++)
     786           0 :         newTasks[i]->activate(rt);
     787             : 
     788           0 :     AutoLockHelperThreadState lock;
     789             : 
     790             :     {
     791           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     792           0 :         if (!HelperThreadState().parseWorklist(lock).appendAll(newTasks))
     793           0 :             oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
     794             :     }
     795             : 
     796           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
     797             : }
     798             : 
     799             : static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
     800             : static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
     801             : 
     802             : // TSan enforces a minimum stack size that's just slightly larger than our
     803             : // default helper stack size.  It does this to store blobs of TSan-specific
     804             : // data on each thread's stack.  Unfortunately, that means that even though
     805             : // we'll actually receive a larger stack than we requested, the effective
     806             : // usable space of that stack is significantly less than what we expect.
     807             : // To offset TSan stealing our stack space from underneath us, double the
     808             : // default.
     809             : //
     810             : // Note that we don't need this for ASan/MOZ_ASAN because ASan doesn't
     811             : // require all the thread-specific state that TSan does.
     812             : #if defined(MOZ_TSAN)
     813             : static const uint32_t HELPER_STACK_SIZE = 2 * kDefaultHelperStackSize;
     814             : static const uint32_t HELPER_STACK_QUOTA = 2 * kDefaultHelperStackQuota;
     815             : #else
     816             : static const uint32_t HELPER_STACK_SIZE = kDefaultHelperStackSize;
     817             : static const uint32_t HELPER_STACK_QUOTA = kDefaultHelperStackQuota;
     818             : #endif
     819             : 
     820             : bool
     821           4 : GlobalHelperThreadState::ensureInitialized()
     822             : {
     823           4 :     MOZ_ASSERT(CanUseExtraThreads());
     824             : 
     825           4 :     MOZ_ASSERT(this == &HelperThreadState());
     826           8 :     AutoLockHelperThreadState lock;
     827             : 
     828           4 :     if (threads)
     829           1 :         return true;
     830             : 
     831           3 :     threads = js::MakeUnique<HelperThreadVector>();
     832           3 :     if (!threads || !threads->initCapacity(threadCount))
     833           0 :         return false;
     834             : 
     835          39 :     for (size_t i = 0; i < threadCount; i++) {
     836          36 :         threads->infallibleEmplaceBack();
     837          36 :         HelperThread& helper = (*threads)[i];
     838             : 
     839          36 :         helper.thread = mozilla::Some(Thread(Thread::Options().setStackSize(HELPER_STACK_SIZE)));
     840          36 :         if (!helper.thread->init(HelperThread::ThreadMain, &helper))
     841           0 :             goto error;
     842             : 
     843          36 :         continue;
     844             : 
     845             :     error:
     846             :         // Ensure that we do not leave uninitialized threads in the `threads`
     847             :         // vector.
     848           0 :         threads->popBack();
     849           0 :         finishThreads();
     850           0 :         return false;
     851             :     }
     852             : 
     853           3 :     return true;
     854             : }
     855             : 
     856           3 : GlobalHelperThreadState::GlobalHelperThreadState()
     857             :  : cpuCount(0),
     858             :    threadCount(0),
     859             :    threads(nullptr),
     860             :    wasmCompilationInProgress(false),
     861             :    numWasmFailedJobs(0),
     862           3 :    helperLock(mutexid::GlobalHelperThreadState)
     863             : {
     864           3 :     cpuCount = GetCPUCount();
     865           3 :     threadCount = ThreadCountForCPUCount(cpuCount);
     866             : 
     867           3 :     MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
     868           3 : }
     869             : 
     870             : void
     871           0 : GlobalHelperThreadState::finish()
     872             : {
     873           0 :     finishThreads();
     874             : 
     875             :     // Make sure there are no Ion free tasks left. We check this here because,
     876             :     // unlike the other tasks, we don't explicitly block on this when
     877             :     // destroying a runtime.
     878           0 :     AutoLockHelperThreadState lock;
     879           0 :     auto& freeList = ionFreeList(lock);
     880           0 :     while (!freeList.empty())
     881           0 :         jit::FreeIonBuilder(freeList.popCopy());
     882           0 : }
     883             : 
     884             : void
     885           0 : GlobalHelperThreadState::finishThreads()
     886             : {
     887           0 :     if (!threads)
     888           0 :         return;
     889             : 
     890           0 :     MOZ_ASSERT(CanUseExtraThreads());
     891           0 :     for (auto& thread : *threads)
     892           0 :         thread.destroy();
     893           0 :     threads.reset(nullptr);
     894             : }
     895             : 
     896             : void
     897           0 : GlobalHelperThreadState::lock()
     898             : {
     899           0 :     helperLock.lock();
     900           0 : }
     901             : 
     902             : void
     903           0 : GlobalHelperThreadState::unlock()
     904             : {
     905           0 :     helperLock.unlock();
     906           0 : }
     907             : 
     908             : #ifdef DEBUG
     909             : bool
     910         554 : GlobalHelperThreadState::isLockedByCurrentThread()
     911             : {
     912         554 :     return helperLock.ownedByCurrentThread();
     913             : }
     914             : #endif // DEBUG
     915             : 
     916             : void
     917          91 : GlobalHelperThreadState::wait(AutoLockHelperThreadState& locked, CondVar which,
     918             :                               TimeDuration timeout /* = TimeDuration::Forever() */)
     919             : {
     920          91 :     whichWakeup(which).wait_for(locked, timeout);
     921          56 : }
     922             : 
     923             : void
     924          70 : GlobalHelperThreadState::notifyAll(CondVar which, const AutoLockHelperThreadState&)
     925             : {
     926          70 :     whichWakeup(which).notify_all();
     927          70 : }
     928             : 
     929             : void
     930          55 : GlobalHelperThreadState::notifyOne(CondVar which, const AutoLockHelperThreadState&)
     931             : {
     932          55 :     whichWakeup(which).notify_one();
     933          55 : }
     934             : 
     935             : bool
     936           0 : GlobalHelperThreadState::hasActiveThreads(const AutoLockHelperThreadState&)
     937             : {
     938           0 :     if (!threads)
     939           0 :         return false;
     940             : 
     941           0 :     for (auto& thread : *threads) {
     942           0 :         if (!thread.idle())
     943           0 :             return true;
     944             :     }
     945             : 
     946           0 :     return false;
     947             : }
     948             : 
     949             : void
     950           0 : GlobalHelperThreadState::waitForAllThreads()
     951             : {
     952           0 :     CancelOffThreadIonCompile();
     953             : 
     954           0 :     AutoLockHelperThreadState lock;
     955           0 :     while (hasActiveThreads(lock))
     956           0 :         wait(lock, CONSUMER);
     957           0 : }
     958             : 
     959             : template <typename T>
     960             : bool
     961         134 : GlobalHelperThreadState::checkTaskThreadLimit(size_t maxThreads) const
     962             : {
     963         134 :     if (maxThreads >= threadCount)
     964          85 :         return true;
     965             : 
     966          49 :     size_t count = 0;
     967         625 :     for (auto& thread : *threads) {
     968         577 :         if (thread.currentTask.isSome() && thread.currentTask->is<T>())
     969           1 :             count++;
     970         577 :         if (count >= maxThreads)
     971           1 :             return false;
     972             :     }
     973             : 
     974          48 :     return true;
     975             : }
     976             : 
     977             : struct MOZ_RAII AutoSetContextRuntime
     978             : {
     979          47 :     explicit AutoSetContextRuntime(JSRuntime* rt) {
     980          47 :         TlsContext.get()->setRuntime(rt);
     981          47 :     }
     982          46 :     ~AutoSetContextRuntime() {
     983          46 :         TlsContext.get()->setRuntime(nullptr);
     984          46 :     }
     985             : };
     986             : 
     987             : static inline bool
     988         134 : IsHelperThreadSimulatingOOM(js::oom::ThreadType threadType)
     989             : {
     990             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     991         134 :     return js::oom::targetThread == threadType;
     992             : #else
     993             :     return false;
     994             : #endif
     995             : }
     996             : 
     997             : size_t
     998          16 : GlobalHelperThreadState::maxIonCompilationThreads() const
     999             : {
    1000          16 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_ION))
    1001           0 :         return 1;
    1002          16 :     return threadCount;
    1003             : }
    1004             : 
    1005             : size_t
    1006          16 : GlobalHelperThreadState::maxUnpausedIonCompilationThreads() const
    1007             : {
    1008          16 :     return 1;
    1009             : }
    1010             : 
    1011             : size_t
    1012           0 : GlobalHelperThreadState::maxWasmCompilationThreads() const
    1013             : {
    1014           0 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_WASM))
    1015           0 :         return 1;
    1016           0 :     return cpuCount;
    1017             : }
    1018             : 
    1019             : size_t
    1020          49 : GlobalHelperThreadState::maxParseThreads() const
    1021             : {
    1022          49 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_PARSE))
    1023           0 :         return 1;
    1024             : 
    1025             :     // Don't allow simultaneous off thread parses, to reduce contention on the
    1026             :     // atoms table. Note that wasm compilation depends on this to avoid
    1027             :     // stalling the helper thread, as off thread parse tasks can trigger and
    1028             :     // block on other off thread wasm compilation tasks.
    1029          49 :     return 1;
    1030             : }
    1031             : 
    1032             : size_t
    1033           0 : GlobalHelperThreadState::maxCompressionThreads() const
    1034             : {
    1035           0 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_COMPRESS))
    1036           0 :         return 1;
    1037             : 
    1038             :     // Compression is triggered on major GCs to compress ScriptSources. It is
    1039             :     // considered low priority work.
    1040           0 :     return 1;
    1041             : }
    1042             : 
    1043             : size_t
    1044           0 : GlobalHelperThreadState::maxGCHelperThreads() const
    1045             : {
    1046           0 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_GCHELPER))
    1047           0 :         return 1;
    1048           0 :     return threadCount;
    1049             : }
    1050             : 
    1051             : size_t
    1052          69 : GlobalHelperThreadState::maxGCParallelThreads() const
    1053             : {
    1054          69 :     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_GCPARALLEL))
    1055           0 :         return 1;
    1056          69 :     return threadCount;
    1057             : }
    1058             : 
    1059             : bool
    1060         161 : GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lock)
    1061             : {
    1062             :     // Don't execute an wasm job if an earlier one failed.
    1063         161 :     if (wasmWorklist(lock).empty() || numWasmFailedJobs)
    1064         161 :         return false;
    1065             : 
    1066             :     // Honor the maximum allowed threads to compile wasm jobs at once,
    1067             :     // to avoid oversaturating the machine.
    1068           0 :     if (!checkTaskThreadLimit<wasm::CompileTask*>(maxWasmCompilationThreads()))
    1069           0 :         return false;
    1070             : 
    1071           0 :     return true;
    1072             : }
    1073             : 
    1074             : bool
    1075         161 : GlobalHelperThreadState::canStartPromiseTask(const AutoLockHelperThreadState& lock)
    1076             : {
    1077         161 :     return !promiseTasks(lock).empty();
    1078             : }
    1079             : 
    1080             : static bool
    1081           1 : IonBuilderHasHigherPriority(jit::IonBuilder* first, jit::IonBuilder* second)
    1082             : {
    1083             :     // This method can return whatever it wants, though it really ought to be a
    1084             :     // total order. The ordering is allowed to race (change on the fly), however.
    1085             : 
    1086             :     // A lower optimization level indicates a higher priority.
    1087           1 :     if (first->optimizationInfo().level() != second->optimizationInfo().level())
    1088           0 :         return first->optimizationInfo().level() < second->optimizationInfo().level();
    1089             : 
    1090             :     // A script without an IonScript has precedence on one with.
    1091           1 :     if (first->scriptHasIonScript() != second->scriptHasIonScript())
    1092           0 :         return !first->scriptHasIonScript();
    1093             : 
    1094             :     // A higher warm-up counter indicates a higher priority.
    1095           1 :     return first->script()->getWarmUpCount() / first->script()->length() >
    1096           1 :            second->script()->getWarmUpCount() / second->script()->length();
    1097             : }
    1098             : 
    1099             : bool
    1100         153 : GlobalHelperThreadState::canStartIonCompile(const AutoLockHelperThreadState& lock)
    1101             : {
    1102         169 :     return !ionWorklist(lock).empty() &&
    1103         169 :            checkTaskThreadLimit<jit::IonBuilder*>(maxIonCompilationThreads());
    1104             : }
    1105             : 
    1106             : bool
    1107         114 : GlobalHelperThreadState::canStartIonFreeTask(const AutoLockHelperThreadState& lock)
    1108             : {
    1109         114 :     return !ionFreeList(lock).empty();
    1110             : }
    1111             : 
    1112             : jit::IonBuilder*
    1113          10 : GlobalHelperThreadState::highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock,
    1114             :                                                           bool remove /* = false */)
    1115             : {
    1116          10 :     auto& worklist = ionWorklist(lock);
    1117          10 :     if (worklist.empty()) {
    1118           1 :         MOZ_ASSERT(!remove);
    1119           1 :         return nullptr;
    1120             :     }
    1121             : 
    1122             :     // Get the highest priority IonBuilder which has not started compilation yet.
    1123           9 :     size_t index = 0;
    1124           9 :     for (size_t i = 1; i < worklist.length(); i++) {
    1125           0 :         if (IonBuilderHasHigherPriority(worklist[i], worklist[index]))
    1126           0 :             index = i;
    1127             :     }
    1128           9 :     jit::IonBuilder* builder = worklist[index];
    1129           9 :     if (remove)
    1130           8 :         worklist.erase(&worklist[index]);
    1131           9 :     return builder;
    1132             : }
    1133             : 
    1134             : HelperThread*
    1135          16 : GlobalHelperThreadState::lowestPriorityUnpausedIonCompileAtThreshold(
    1136             :     const AutoLockHelperThreadState& lock)
    1137             : {
    1138             :     // Get the lowest priority IonBuilder which has started compilation and
    1139             :     // isn't paused, unless there are still fewer than the maximum number of
    1140             :     // such builders permitted.
    1141          16 :     size_t numBuilderThreads = 0;
    1142          16 :     HelperThread* thread = nullptr;
    1143         208 :     for (auto& thisThread : *threads) {
    1144         192 :         if (thisThread.ionBuilder() && !thisThread.pause) {
    1145           2 :             numBuilderThreads++;
    1146           2 :             if (!thread ||
    1147           0 :                 IonBuilderHasHigherPriority(thread->ionBuilder(), thisThread.ionBuilder()))
    1148             :             {
    1149           2 :                 thread = &thisThread;
    1150             :             }
    1151             :         }
    1152             :     }
    1153          16 :     if (numBuilderThreads < maxUnpausedIonCompilationThreads())
    1154          14 :         return nullptr;
    1155           2 :     return thread;
    1156             : }
    1157             : 
    1158             : HelperThread*
    1159           8 : GlobalHelperThreadState::highestPriorityPausedIonCompile(const AutoLockHelperThreadState& lock)
    1160             : {
    1161             :     // Get the highest priority IonBuilder which has started compilation but
    1162             :     // which was subsequently paused.
    1163           8 :     HelperThread* thread = nullptr;
    1164         104 :     for (auto& thisThread : *threads) {
    1165          96 :         if (thisThread.pause) {
    1166             :             // Currently, only threads with IonBuilders can be paused.
    1167           1 :             MOZ_ASSERT(thisThread.ionBuilder());
    1168           1 :             if (!thread ||
    1169           0 :                 IonBuilderHasHigherPriority(thisThread.ionBuilder(), thread->ionBuilder()))
    1170             :             {
    1171           1 :                 thread = &thisThread;
    1172             :             }
    1173             :         }
    1174             :     }
    1175           8 :     return thread;
    1176             : }
    1177             : 
    1178             : bool
    1179         145 : GlobalHelperThreadState::pendingIonCompileHasSufficientPriority(
    1180             :     const AutoLockHelperThreadState& lock)
    1181             : {
    1182             :     // Can't compile anything if there are no scripts to compile.
    1183         145 :     if (!canStartIonCompile(lock))
    1184         137 :         return false;
    1185             : 
    1186             :     // Count the number of threads currently compiling scripts, and look for
    1187             :     // the thread with the lowest priority.
    1188           8 :     HelperThread* lowestPriorityThread = lowestPriorityUnpausedIonCompileAtThreshold(lock);
    1189             : 
    1190             :     // If the number of threads building scripts is less than the maximum, the
    1191             :     // compilation can start immediately.
    1192           8 :     if (!lowestPriorityThread)
    1193           7 :         return true;
    1194             : 
    1195             :     // If there is a builder in the worklist with higher priority than some
    1196             :     // builder currently being compiled, then that current compilation can be
    1197             :     // paused, so allow the compilation.
    1198           1 :     if (IonBuilderHasHigherPriority(highestPriorityPendingIonCompile(lock),
    1199             :                                     lowestPriorityThread->ionBuilder()))
    1200           1 :         return true;
    1201             : 
    1202             :     // Compilation will have to wait until one of the active compilations finishes.
    1203           0 :     return false;
    1204             : }
    1205             : 
    1206             : bool
    1207         177 : GlobalHelperThreadState::canStartParseTask(const AutoLockHelperThreadState& lock)
    1208             : {
    1209         177 :     return !parseWorklist(lock).empty() && checkTaskThreadLimit<ParseTask*>(maxParseThreads());
    1210             : }
    1211             : 
    1212             : bool
    1213         130 : GlobalHelperThreadState::canStartCompressionTask(const AutoLockHelperThreadState& lock)
    1214             : {
    1215         130 :     return !compressionWorklist(lock).empty() &&
    1216         130 :            checkTaskThreadLimit<SourceCompressionTask*>(maxCompressionThreads());
    1217             : }
    1218             : 
    1219             : void
    1220           1 : GlobalHelperThreadState::startHandlingCompressionTasks(const AutoLockHelperThreadState& lock)
    1221             : {
    1222           1 :     scheduleCompressionTasks(lock);
    1223           1 :     if (canStartCompressionTask(lock))
    1224           0 :         notifyOne(PRODUCER, lock);
    1225           1 : }
    1226             : 
    1227             : void
    1228           1 : GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadState& lock)
    1229             : {
    1230           1 :     auto& pending = compressionPendingList(lock);
    1231           1 :     auto& worklist = compressionWorklist(lock);
    1232             : 
    1233          50 :     for (size_t i = 0; i < pending.length(); i++) {
    1234          49 :         if (pending[i]->shouldStart()) {
    1235             :             // OOMing during appending results in the task not being scheduled
    1236             :             // and deleted.
    1237           0 :             Unused << worklist.append(Move(pending[i]));
    1238           0 :             remove(pending, &i);
    1239             :         }
    1240             :     }
    1241           1 : }
    1242             : 
    1243             : bool
    1244         153 : GlobalHelperThreadState::canStartGCHelperTask(const AutoLockHelperThreadState& lock)
    1245             : {
    1246         153 :     return !gcHelperWorklist(lock).empty() &&
    1247         153 :            checkTaskThreadLimit<GCHelperState*>(maxGCHelperThreads());
    1248             : }
    1249             : 
    1250             : bool
    1251         199 : GlobalHelperThreadState::canStartGCParallelTask(const AutoLockHelperThreadState& lock)
    1252             : {
    1253         268 :     return !gcParallelWorklist(lock).empty() &&
    1254         268 :            checkTaskThreadLimit<GCParallelTask*>(maxGCParallelThreads());
    1255             : }
    1256             : 
    1257           4 : js::GCParallelTask::~GCParallelTask()
    1258             : {
    1259             :     // Only most-derived classes' destructors may do the join: base class
    1260             :     // destructors run after those for derived classes' members, so a join in a
    1261             :     // base class can't ensure that the task is done using the members. All we
    1262             :     // can do now is check that someone has previously stopped the task.
    1263             : #ifdef DEBUG
    1264           4 :     mozilla::Maybe<AutoLockHelperThreadState> helperLock;
    1265           2 :     if (!HelperThreadState().isLockedByCurrentThread())
    1266           0 :         helperLock.emplace();
    1267           2 :     MOZ_ASSERT(state == NotStarted);
    1268             : #endif
    1269           2 : }
    1270             : 
    1271             : bool
    1272          23 : js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
    1273             : {
    1274             :     // Tasks cannot be started twice.
    1275          23 :     MOZ_ASSERT(state == NotStarted);
    1276             : 
    1277             :     // If we do the shutdown GC before running anything, we may never
    1278             :     // have initialized the helper threads. Just use the serial path
    1279             :     // since we cannot safely intialize them at this point.
    1280          23 :     if (!HelperThreadState().threads)
    1281           0 :         return false;
    1282             : 
    1283          23 :     if (!HelperThreadState().gcParallelWorklist(lock).append(this))
    1284           0 :         return false;
    1285          23 :     state = Dispatched;
    1286             : 
    1287          23 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
    1288             : 
    1289          23 :     return true;
    1290             : }
    1291             : 
    1292             : bool
    1293           0 : js::GCParallelTask::start()
    1294             : {
    1295           0 :     AutoLockHelperThreadState helperLock;
    1296           0 :     return startWithLockHeld(helperLock);
    1297             : }
    1298             : 
    1299             : void
    1300          26 : js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& locked)
    1301             : {
    1302          26 :     if (state == NotStarted)
    1303           4 :         return;
    1304             : 
    1305           0 :     while (state != Finished)
    1306           0 :         HelperThreadState().wait(locked, GlobalHelperThreadState::CONSUMER);
    1307          22 :     state = NotStarted;
    1308          22 :     cancel_ = false;
    1309             : }
    1310             : 
    1311             : void
    1312           3 : js::GCParallelTask::join()
    1313             : {
    1314           6 :     AutoLockHelperThreadState helperLock;
    1315           3 :     joinWithLockHeld(helperLock);
    1316           3 : }
    1317             : 
    1318             : void
    1319           0 : js::GCParallelTask::runFromActiveCooperatingThread(JSRuntime* rt)
    1320             : {
    1321           0 :     MOZ_ASSERT(state == NotStarted);
    1322           0 :     MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
    1323           0 :     mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
    1324           0 :     run();
    1325           0 :     duration_ = mozilla::TimeStamp::Now() - timeStart;
    1326           0 : }
    1327             : 
    1328             : void
    1329          23 : js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
    1330             : {
    1331          46 :     AutoSetContextRuntime ascr(runtime());
    1332          46 :     gc::AutoSetThreadIsPerformingGC performingGC;
    1333             : 
    1334             :     {
    1335          46 :         AutoUnlockHelperThreadState parallelSection(locked);
    1336          23 :         mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now();
    1337          23 :         TlsContext.get()->heapState = JS::HeapState::MajorCollecting;
    1338          23 :         run();
    1339          23 :         TlsContext.get()->heapState = JS::HeapState::Idle;
    1340          23 :         duration_ = mozilla::TimeStamp::Now() - timeStart;
    1341             :     }
    1342             : 
    1343          23 :     state = Finished;
    1344          23 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1345          23 : }
    1346             : 
    1347             : bool
    1348          22 : js::GCParallelTask::isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const
    1349             : {
    1350          22 :     return state == Dispatched;
    1351             : }
    1352             : 
    1353             : bool
    1354           1 : js::GCParallelTask::isRunning() const
    1355             : {
    1356           2 :     AutoLockHelperThreadState helperLock;
    1357           2 :     return isRunningWithLockHeld(helperLock);
    1358             : }
    1359             : 
    1360             : void
    1361          23 : HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& locked)
    1362             : {
    1363          23 :     MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(locked));
    1364          23 :     MOZ_ASSERT(idle());
    1365             : 
    1366          23 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    1367          46 :     AutoTraceLog logCompile(logger, TraceLogger_GC);
    1368             : 
    1369          23 :     currentTask.emplace(HelperThreadState().gcParallelWorklist(locked).popCopy());
    1370          23 :     gcParallelTask()->runFromHelperThread(locked);
    1371          23 :     currentTask.reset();
    1372          23 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1373          23 : }
    1374             : 
    1375             : static void
    1376          15 : LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
    1377             : {
    1378             :     // Mark the zone as no longer in use by a helper thread, and available
    1379             :     // to be collected by the GC.
    1380          15 :     rt->clearUsedByHelperThread(task->parseGlobal->zone());
    1381          15 : }
    1382             : 
    1383             : ParseTask*
    1384          15 : GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, void* token)
    1385             : {
    1386             :     // The token is a ParseTask* which should be in the finished list.
    1387             :     // Find and remove its entry.
    1388             : 
    1389          30 :     AutoLockHelperThreadState lock;
    1390          15 :     ParseTaskVector& finished = parseFinishedList(lock);
    1391             : 
    1392          15 :     for (size_t i = 0; i < finished.length(); i++) {
    1393          15 :         if (finished[i] == token) {
    1394          15 :             ParseTask* parseTask = finished[i];
    1395          15 :             remove(finished, &i);
    1396          15 :             MOZ_ASSERT(parseTask);
    1397          15 :             MOZ_ASSERT(parseTask->kind == kind);
    1398          30 :             return parseTask;
    1399             :         }
    1400             :     }
    1401             : 
    1402           0 :     MOZ_CRASH("Invalid ParseTask token");
    1403             : }
    1404             : 
    1405             : template <typename F, typename>
    1406             : bool
    1407          15 : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback)
    1408             : {
    1409          15 :     MOZ_ASSERT(cx->compartment());
    1410             : 
    1411          30 :     ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
    1412             : 
    1413             :     // Make sure we have all the constructors we need for the prototype
    1414             :     // remapping below, since we can't GC while that's happening.
    1415          30 :     Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
    1416          15 :     if (!EnsureParserCreatedClasses(cx, kind)) {
    1417           0 :         LeaveParseTaskZone(cx->runtime(), parseTask);
    1418           0 :         return false;
    1419             :     }
    1420             : 
    1421          15 :     mergeParseTaskCompartment(cx, parseTask, global, cx->compartment());
    1422             : 
    1423          15 :     bool ok = finishCallback(parseTask);
    1424             : 
    1425         191 :     for (auto& script : parseTask->scripts)
    1426         176 :         releaseAssertSameCompartment(cx, script);
    1427             : 
    1428          15 :     if (!parseTask->finish(cx) || !ok)
    1429           0 :         return false;
    1430             : 
    1431             :     // Report out of memory errors eagerly, or errors could be malformed.
    1432          15 :     if (parseTask->outOfMemory) {
    1433           0 :         ReportOutOfMemory(cx);
    1434           0 :         return false;
    1435             :     }
    1436             : 
    1437             :     // Report any error or warnings generated during the parse, and inform the
    1438             :     // debugger about the compiled scripts.
    1439          15 :     for (size_t i = 0; i < parseTask->errors.length(); i++)
    1440           0 :         parseTask->errors[i]->throwError(cx);
    1441          15 :     if (parseTask->overRecursed)
    1442           0 :         ReportOverRecursed(cx);
    1443          15 :     if (cx->isExceptionPending())
    1444           0 :         return false;
    1445             : 
    1446          15 :     return true;
    1447             : }
    1448             : 
    1449             : JSScript*
    1450           0 : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
    1451             : {
    1452           0 :     JS::RootedScript script(cx);
    1453             : 
    1454           0 :     bool ok = finishParseTask(cx, kind, token, [&script] (ParseTask* parseTask) {
    1455           0 :         MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
    1456             : 
    1457           0 :         if (parseTask->scripts.length() > 0)
    1458           0 :             script = parseTask->scripts[0];
    1459             : 
    1460           0 :         return true;
    1461           0 :     });
    1462             : 
    1463           0 :     if (!ok)
    1464           0 :         return nullptr;
    1465             : 
    1466           0 :     if (!script) {
    1467             :         // No error was reported, but no script produced. Assume we hit out of
    1468             :         // memory.
    1469           0 :         ReportOutOfMemory(cx);
    1470           0 :         return nullptr;
    1471             :     }
    1472             : 
    1473             :     // The Debugger only needs to be told about the topmost script that was compiled.
    1474           0 :     Debugger::onNewScript(cx, script);
    1475             : 
    1476           0 :     return script;
    1477             : }
    1478             : 
    1479             : bool
    1480          15 : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token,
    1481             :                                          MutableHandle<ScriptVector> scripts)
    1482             : {
    1483          15 :     size_t expectedLength = 0;
    1484             : 
    1485         236 :     bool ok = finishParseTask(cx, kind, token, [&scripts, &expectedLength] (ParseTask* parseTask) {
    1486          15 :         expectedLength = parseTask->data.as<JS::TranscodeSources*>()->length();
    1487             : 
    1488          30 :         if (!scripts.reserve(parseTask->scripts.length()))
    1489           0 :             return false;
    1490             : 
    1491         191 :         for (auto& script : parseTask->scripts)
    1492         176 :             scripts.infallibleAppend(script);
    1493          15 :         return true;
    1494          15 :     });
    1495             : 
    1496          15 :     if (!ok)
    1497           0 :         return false;
    1498             : 
    1499          15 :     if (scripts.length() != expectedLength) {
    1500             :         // No error was reported, but fewer scripts produced than expected.
    1501             :         // Assume we hit out of memory.
    1502           0 :         ReportOutOfMemory(cx);
    1503           0 :         return false;
    1504             :     }
    1505             : 
    1506             :     // The Debugger only needs to be told about the topmost script that was compiled.
    1507          30 :     JS::RootedScript rooted(cx);
    1508         191 :     for (auto& script : scripts) {
    1509         176 :         MOZ_ASSERT(script->isGlobalCode());
    1510             : 
    1511         176 :         rooted = script;
    1512         176 :         Debugger::onNewScript(cx, rooted);
    1513             :     }
    1514             : 
    1515          15 :     return true;
    1516             : }
    1517             : 
    1518             : JSScript*
    1519           0 : GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
    1520             : {
    1521           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::Script, token);
    1522           0 :     MOZ_ASSERT_IF(script, script->isGlobalCode());
    1523           0 :     return script;
    1524             : }
    1525             : 
    1526             : JSScript*
    1527           0 : GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
    1528             : {
    1529           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
    1530           0 :     MOZ_ASSERT_IF(script, script->isGlobalCode());
    1531           0 :     return script;
    1532             : }
    1533             : 
    1534             : bool
    1535          15 : GlobalHelperThreadState::finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts)
    1536             : {
    1537          15 :     return finishParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
    1538             : }
    1539             : 
    1540             : JSObject*
    1541           0 : GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
    1542             : {
    1543           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::Module, token);
    1544           0 :     if (!script)
    1545           0 :         return nullptr;
    1546             : 
    1547           0 :     MOZ_ASSERT(script->module());
    1548             : 
    1549           0 :     RootedModuleObject module(cx, script->module());
    1550           0 :     module->fixEnvironmentsAfterCompartmentMerge();
    1551           0 :     if (!ModuleObject::Freeze(cx, module))
    1552           0 :         return nullptr;
    1553             : 
    1554           0 :     return module;
    1555             : }
    1556             : 
    1557             : void
    1558           0 : GlobalHelperThreadState::cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token)
    1559             : {
    1560           0 :     ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
    1561           0 :     LeaveParseTaskZone(rt, parseTask);
    1562           0 : }
    1563             : 
    1564             : JSObject*
    1565         229 : GlobalObject::getStarGeneratorFunctionPrototype()
    1566             : {
    1567         229 :     const Value& v = getReservedSlot(STAR_GENERATOR_FUNCTION_PROTO);
    1568         229 :     return v.isObject() ? &v.toObject() : nullptr;
    1569             : }
    1570             : 
    1571             : void
    1572          15 : GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
    1573             :                                                    Handle<GlobalObject*> global,
    1574             :                                                    JSCompartment* dest)
    1575             : {
    1576             :     // Finish any ongoing incremental GC that may affect the destination zone.
    1577          15 :     if (JS::IsIncrementalGCInProgress(cx) && dest->zone()->wasGCStarted())
    1578           0 :         JS::FinishIncrementalGC(cx, JS::gcreason::API);
    1579             : 
    1580             :     // After we call LeaveParseTaskZone() it's not safe to GC until we have
    1581             :     // finished merging the contents of the parse task's compartment into the
    1582             :     // destination compartment.
    1583          30 :     JS::AutoAssertNoGC nogc(cx);
    1584             : 
    1585          15 :     LeaveParseTaskZone(cx->runtime(), parseTask);
    1586          30 :     AutoCompartment ac(cx, parseTask->parseGlobal);
    1587             : 
    1588             :     {
    1589             :         // Generator functions don't have Function.prototype as prototype but a
    1590             :         // different function object, so the IdentifyStandardPrototype trick
    1591             :         // below won't work.  Just special-case it.
    1592          15 :         GlobalObject* parseGlobal = &parseTask->parseGlobal->as<GlobalObject>();
    1593          15 :         JSObject* parseTaskStarGenFunctionProto = parseGlobal->getStarGeneratorFunctionPrototype();
    1594             : 
    1595             :         // Module objects don't have standard prototypes either.
    1596          15 :         JSObject* moduleProto = parseGlobal->maybeGetModulePrototype();
    1597          15 :         JSObject* importEntryProto = parseGlobal->maybeGetImportEntryPrototype();
    1598          15 :         JSObject* exportEntryProto = parseGlobal->maybeGetExportEntryPrototype();
    1599             : 
    1600             :         // Point the prototypes of any objects in the script's compartment to refer
    1601             :         // to the corresponding prototype in the new compartment. This will briefly
    1602             :         // create cross compartment pointers, which will be fixed by the
    1603             :         // MergeCompartments call below.
    1604          15 :         Zone* parseZone = parseTask->parseGlobal->zone();
    1605        7056 :         for (auto group = parseZone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
    1606        7041 :             TaggedProto proto(group->proto());
    1607        7041 :             if (!proto.isObject())
    1608         300 :                 continue;
    1609             : 
    1610        6921 :             JSObject* protoObj = proto.toObject();
    1611             : 
    1612             :             JSObject* newProto;
    1613        6921 :             JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
    1614        6921 :             if (key != JSProto_Null) {
    1615        6647 :                 MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
    1616             :                            key == JSProto_Function || key == JSProto_RegExp ||
    1617             :                            key == JSProto_Iterator);
    1618        6647 :                 newProto = GetBuiltinPrototypePure(global, key);
    1619         274 :             } else if (protoObj == parseTaskStarGenFunctionProto) {
    1620         214 :                 newProto = global->getStarGeneratorFunctionPrototype();
    1621          60 :             } else if (protoObj == moduleProto) {
    1622           0 :                 newProto = global->getModulePrototype();
    1623          60 :             } else if (protoObj == importEntryProto) {
    1624           0 :                 newProto = global->getImportEntryPrototype();
    1625          60 :             } else if (protoObj == exportEntryProto) {
    1626           0 :                 newProto = global->getExportEntryPrototype();
    1627             :             } else {
    1628          60 :                 continue;
    1629             :             }
    1630             : 
    1631        6861 :             group->setProtoUnchecked(TaggedProto(newProto));
    1632             :         }
    1633             :     }
    1634             : 
    1635             :     // Move the parsed script and all its contents into the desired compartment.
    1636          15 :     gc::MergeCompartments(parseTask->parseGlobal->compartment(), dest);
    1637          15 : }
    1638             : 
    1639             : void
    1640           0 : HelperThread::destroy()
    1641             : {
    1642           0 :     if (thread.isSome()) {
    1643             :         {
    1644           0 :             AutoLockHelperThreadState lock;
    1645           0 :             terminate = true;
    1646             : 
    1647             :             /* Notify all helpers, to ensure that this thread wakes up. */
    1648           0 :             HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
    1649             :         }
    1650             : 
    1651           0 :         thread->join();
    1652           0 :         thread.reset();
    1653             :     }
    1654           0 : }
    1655             : 
    1656             : /* static */
    1657             : void
    1658          34 : HelperThread::ThreadMain(void* arg)
    1659             : {
    1660          34 :     ThisThread::SetName("JS Helper");
    1661             : 
    1662          36 :     static_cast<HelperThread*>(arg)->threadLoop();
    1663           0 :     Mutex::ShutDown();
    1664           0 : }
    1665             : 
    1666             : void
    1667           0 : HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked)
    1668             : {
    1669           0 :     MOZ_ASSERT(HelperThreadState().canStartWasmCompile(locked));
    1670           0 :     MOZ_ASSERT(idle());
    1671             : 
    1672           0 :     currentTask.emplace(HelperThreadState().wasmWorklist(locked).popCopy());
    1673           0 :     bool success = false;
    1674           0 :     UniqueChars error;
    1675             : 
    1676           0 :     wasm::CompileTask* task = wasmTask();
    1677             :     {
    1678           0 :         AutoUnlockHelperThreadState unlock(locked);
    1679           0 :         success = wasm::CompileFunction(task, &error);
    1680             :     }
    1681             : 
    1682             :     // On success, try to move work to the finished list.
    1683           0 :     if (success)
    1684           0 :         success = HelperThreadState().wasmFinishedList(locked).append(task);
    1685             : 
    1686             :     // On failure, note the failure for harvesting by the parent.
    1687           0 :     if (!success) {
    1688           0 :         HelperThreadState().noteWasmFailure(locked);
    1689           0 :         HelperThreadState().setWasmError(locked, Move(error));
    1690             :     }
    1691             : 
    1692             :     // Notify the active thread in case it's waiting.
    1693           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1694           0 :     currentTask.reset();
    1695           0 : }
    1696             : 
    1697             : void
    1698           0 : HelperThread::handlePromiseTaskWorkload(AutoLockHelperThreadState& locked)
    1699             : {
    1700           0 :     MOZ_ASSERT(HelperThreadState().canStartPromiseTask(locked));
    1701           0 :     MOZ_ASSERT(idle());
    1702             : 
    1703           0 :     PromiseTask* task = HelperThreadState().promiseTasks(locked).popCopy();
    1704           0 :     currentTask.emplace(task);
    1705             : 
    1706             :     {
    1707           0 :         AutoUnlockHelperThreadState unlock(locked);
    1708             : 
    1709           0 :         task->execute();
    1710             : 
    1711           0 :         if (!task->runtime()->finishAsyncTaskCallback(task)) {
    1712             :             // We cannot simply delete the task now because the PromiseTask must
    1713             :             // be destroyed on its runtime's thread. Add it to a list of tasks
    1714             :             // to delete before the next GC.
    1715           0 :             AutoEnterOOMUnsafeRegion oomUnsafe;
    1716           0 :             if (!task->runtime()->promiseTasksToDestroy.lock()->append(task))
    1717           0 :                 oomUnsafe.crash("handlePromiseTaskWorkload");
    1718             :         }
    1719             :     }
    1720             : 
    1721             :     // Notify the active thread in case it's waiting.
    1722           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1723           0 :     currentTask.reset();
    1724           0 : }
    1725             : 
    1726             : void
    1727           8 : HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
    1728             : {
    1729           8 :     MOZ_ASSERT(HelperThreadState().canStartIonCompile(locked));
    1730           8 :     MOZ_ASSERT(idle());
    1731             : 
    1732             :     // Find the IonBuilder in the worklist with the highest priority, and
    1733             :     // remove it from the worklist.
    1734             :     jit::IonBuilder* builder =
    1735           8 :         HelperThreadState().highestPriorityPendingIonCompile(locked, /* remove = */ true);
    1736             : 
    1737             :     // If there are now too many threads with active IonBuilders, indicate to
    1738             :     // the one with the lowest priority that it should pause. Note that due to
    1739             :     // builder priorities changing since pendingIonCompileHasSufficientPriority
    1740             :     // was called, the builder we are pausing may actually be higher priority
    1741             :     // than the one we are about to start. Oh well.
    1742           8 :     HelperThread* other = HelperThreadState().lowestPriorityUnpausedIonCompileAtThreshold(locked);
    1743           8 :     if (other) {
    1744           1 :         MOZ_ASSERT(other->ionBuilder() && !other->pause);
    1745           1 :         other->pause = true;
    1746             :     }
    1747             : 
    1748           8 :     currentTask.emplace(builder);
    1749           8 :     builder->setPauseFlag(&pause);
    1750             : 
    1751           8 :     JSRuntime* rt = builder->script()->compartment()->runtimeFromAnyThread();
    1752             : 
    1753             :     {
    1754          16 :         AutoUnlockHelperThreadState unlock(locked);
    1755             : 
    1756           8 :         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    1757          16 :         TraceLoggerEvent event(TraceLogger_AnnotateScripts, builder->script());
    1758          16 :         AutoTraceLog logScript(logger, event);
    1759          16 :         AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
    1760             : 
    1761          16 :         AutoSetContextRuntime ascr(rt);
    1762             :         jit::JitContext jctx(jit::CompileRuntime::get(rt),
    1763             :                              jit::CompileCompartment::get(builder->script()->compartment()),
    1764          16 :                              &builder->alloc());
    1765           8 :         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
    1766             :     }
    1767             : 
    1768           8 :     FinishOffThreadIonCompile(builder, locked);
    1769             : 
    1770             :     // Ping any thread currently operating on the compiled script's zone group
    1771             :     // so that the compiled code can be incorporated at the next interrupt
    1772             :     // callback. Don't interrupt Ion code for this, as this incorporation can
    1773             :     // be delayed indefinitely without affecting performance as long as the
    1774             :     // active thread is actually executing Ion code.
    1775             :     //
    1776             :     // This must happen before the current task is reset. DestroyContext
    1777             :     // cancels in progress Ion compilations before destroying its target
    1778             :     // context, and after we reset the current task we are no longer considered
    1779             :     // to be Ion compiling.
    1780           8 :     JSContext* target = builder->script()->zoneFromAnyThread()->group()->ownerContext().context();
    1781           8 :     if (target)
    1782           8 :         target->requestInterrupt(JSContext::RequestInterruptCanWait);
    1783             : 
    1784           8 :     currentTask.reset();
    1785           8 :     pause = false;
    1786             : 
    1787             :     // Notify the active thread in case it is waiting for the compilation to finish.
    1788           8 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1789             : 
    1790             :     // When finishing Ion compilation jobs, we can start unpausing compilation
    1791             :     // threads that were paused to restrict the number of active compilations.
    1792             :     // Only unpause one at a time, to make sure we don't exceed the restriction.
    1793             :     // Since threads are currently only paused for Ion compilations, this
    1794             :     // strategy will eventually unpause all paused threads, regardless of how
    1795             :     // many there are, since each thread we unpause will eventually finish and
    1796             :     // end up back here.
    1797           8 :     if (HelperThread* other = HelperThreadState().highestPriorityPausedIonCompile(locked)) {
    1798           1 :         MOZ_ASSERT(other->ionBuilder() && other->pause);
    1799             : 
    1800             :         // Only unpause the other thread if there isn't a higher priority
    1801             :         // builder which this thread or another can start on.
    1802           1 :         jit::IonBuilder* builder = HelperThreadState().highestPriorityPendingIonCompile(locked);
    1803           1 :         if (!builder || IonBuilderHasHigherPriority(other->ionBuilder(), builder)) {
    1804           1 :             other->pause = false;
    1805             : 
    1806             :             // Notify all paused threads, to make sure the one we just
    1807             :             // unpaused wakes up.
    1808           1 :             HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE, locked);
    1809             :         }
    1810             :     }
    1811           8 : }
    1812             : 
    1813             : void
    1814           8 : HelperThread::handleIonFreeWorkload(AutoLockHelperThreadState& locked)
    1815             : {
    1816           8 :     MOZ_ASSERT(idle());
    1817           8 :     MOZ_ASSERT(HelperThreadState().canStartIonFreeTask(locked));
    1818             : 
    1819           8 :     auto& freeList = HelperThreadState().ionFreeList(locked);
    1820             : 
    1821           8 :     jit::IonBuilder* builder = freeList.popCopy();
    1822             :     {
    1823          16 :         AutoUnlockHelperThreadState unlock(locked);
    1824           8 :         FreeIonBuilder(builder);
    1825             :     }
    1826           8 : }
    1827             : 
    1828             : HelperThread*
    1829          23 : js::CurrentHelperThread()
    1830             : {
    1831          23 :     if (!HelperThreadState().threads)
    1832           0 :         return nullptr;
    1833          23 :     auto threadId = ThisThread::GetId();
    1834         175 :     for (auto& thisThread : *HelperThreadState().threads) {
    1835         171 :         if (thisThread.thread.isSome() && threadId == thisThread.thread->get_id())
    1836          19 :             return &thisThread;
    1837             :     }
    1838           4 :     return nullptr;
    1839             : }
    1840             : 
    1841             : void
    1842           1 : js::PauseCurrentHelperThread()
    1843             : {
    1844           1 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    1845           2 :     AutoTraceLog logPaused(logger, TraceLogger_IonCompilationPaused);
    1846             : 
    1847           1 :     HelperThread* thread = CurrentHelperThread();
    1848           1 :     MOZ_ASSERT(thread);
    1849             : 
    1850           2 :     AutoLockHelperThreadState lock;
    1851           3 :     while (thread->pause)
    1852           1 :         HelperThreadState().wait(lock, GlobalHelperThreadState::PAUSE);
    1853           1 : }
    1854             : 
    1855             : bool
    1856           0 : JSContext::addPendingCompileError(js::CompileError** error)
    1857             : {
    1858           0 :     auto errorPtr = make_unique<js::CompileError>();
    1859           0 :     if (!errorPtr)
    1860           0 :         return false;
    1861           0 :     if (!helperThread()->parseTask()->errors.append(errorPtr.get())) {
    1862           0 :         ReportOutOfMemory(this);
    1863           0 :         return false;
    1864             :     }
    1865           0 :     *error = errorPtr.release();
    1866           0 :     return true;
    1867             : }
    1868             : 
    1869             : void
    1870           0 : JSContext::addPendingOverRecursed()
    1871             : {
    1872           0 :     if (helperThread()->parseTask())
    1873           0 :         helperThread()->parseTask()->overRecursed = true;
    1874           0 : }
    1875             : 
    1876             : void
    1877           0 : JSContext::addPendingOutOfMemory()
    1878             : {
    1879             :     // Keep in sync with recoverFromOutOfMemory.
    1880           0 :     if (helperThread()->parseTask())
    1881           0 :         helperThread()->parseTask()->outOfMemory = true;
    1882           0 : }
    1883             : 
    1884             : void
    1885          16 : HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked)
    1886             : {
    1887          16 :     MOZ_ASSERT(HelperThreadState().canStartParseTask(locked));
    1888          16 :     MOZ_ASSERT(idle());
    1889             : 
    1890          16 :     currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
    1891          16 :     ParseTask* task = parseTask();
    1892             : 
    1893             :     {
    1894          31 :         AutoUnlockHelperThreadState unlock(locked);
    1895          31 :         AutoSetContextRuntime ascr(task->parseGlobal->runtimeFromAnyThread());
    1896             : 
    1897          16 :         JSContext* cx = TlsContext.get();
    1898          31 :         AutoCompartment ac(cx, task->parseGlobal);
    1899             : 
    1900          16 :         task->parse(cx);
    1901             :     }
    1902             : 
    1903             :     // The callback is invoked while we are still off thread.
    1904          15 :     task->callback(task, task->callbackData);
    1905             : 
    1906             :     // FinishOffThreadScript will need to be called on the script to
    1907             :     // migrate it into the correct compartment.
    1908             :     {
    1909          30 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    1910          15 :         if (!HelperThreadState().parseFinishedList(locked).append(task))
    1911           0 :             oomUnsafe.crash("handleParseWorkload");
    1912             :     }
    1913             : 
    1914          15 :     currentTask.reset();
    1915             : 
    1916             :     // Notify the active thread in case it is waiting for the parse/emit to finish.
    1917          15 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1918          15 : }
    1919             : 
    1920             : void
    1921           0 : HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
    1922             : {
    1923           0 :     MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
    1924           0 :     MOZ_ASSERT(idle());
    1925             : 
    1926           0 :     UniquePtr<SourceCompressionTask> task;
    1927             :     {
    1928           0 :         auto& worklist = HelperThreadState().compressionWorklist(locked);
    1929           0 :         task = Move(worklist.back());
    1930           0 :         worklist.popBack();
    1931           0 :         currentTask.emplace(task.get());
    1932             :     }
    1933             : 
    1934             :     {
    1935           0 :         AutoUnlockHelperThreadState unlock(locked);
    1936             : 
    1937           0 :         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    1938           0 :         AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
    1939             : 
    1940           0 :         task->work();
    1941             :     }
    1942             : 
    1943             :     {
    1944           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    1945           0 :         if (!HelperThreadState().compressionFinishedList(locked).append(Move(task)))
    1946           0 :             oomUnsafe.crash("handleCompressionWorkload");
    1947             :     }
    1948             : 
    1949           0 :     currentTask.reset();
    1950             : 
    1951             :     // Notify the active thread in case it is waiting for the compression to finish.
    1952           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1953           0 : }
    1954             : 
    1955             : bool
    1956         154 : js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
    1957             : {
    1958         308 :     AutoLockHelperThreadState lock;
    1959             : 
    1960         154 :     auto& pending = HelperThreadState().compressionPendingList(lock);
    1961         154 :     if (!pending.append(Move(task))) {
    1962           0 :         if (!cx->helperThread())
    1963           0 :             ReportOutOfMemory(cx);
    1964           0 :         return false;
    1965             :     }
    1966             : 
    1967         154 :     return true;
    1968             : }
    1969             : 
    1970             : template <typename T>
    1971             : static void
    1972           0 : ClearCompressionTaskList(T& list, JSRuntime* runtime)
    1973             : {
    1974           0 :     for (size_t i = 0; i < list.length(); i++) {
    1975           0 :         if (list[i]->runtimeMatches(runtime))
    1976           0 :             HelperThreadState().remove(list, &i);
    1977             :     }
    1978           0 : }
    1979             : 
    1980             : void
    1981           0 : js::CancelOffThreadCompressions(JSRuntime* runtime)
    1982             : {
    1983           0 :     AutoLockHelperThreadState lock;
    1984             : 
    1985           0 :     if (!HelperThreadState().threads)
    1986           0 :         return;
    1987             : 
    1988             :     // Cancel all pending compression tasks.
    1989           0 :     ClearCompressionTaskList(HelperThreadState().compressionPendingList(lock), runtime);
    1990           0 :     ClearCompressionTaskList(HelperThreadState().compressionWorklist(lock), runtime);
    1991             : 
    1992             :     // Cancel all in-process compression tasks and wait for them to join so we
    1993             :     // clean up the finished tasks.
    1994             :     while (true) {
    1995           0 :         bool inProgress = false;
    1996           0 :         for (auto& thread : *HelperThreadState().threads) {
    1997           0 :             SourceCompressionTask* task = thread.compressionTask();
    1998           0 :             if (task && task->runtimeMatches(runtime))
    1999           0 :                 inProgress = true;
    2000             :         }
    2001             : 
    2002           0 :         if (!inProgress)
    2003           0 :             break;
    2004             : 
    2005           0 :         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
    2006           0 :     }
    2007             : 
    2008             :     // Clean up finished tasks.
    2009           0 :     ClearCompressionTaskList(HelperThreadState().compressionFinishedList(lock), runtime);
    2010             : }
    2011             : 
    2012             : bool
    2013           0 : js::StartPromiseTask(JSContext* cx, UniquePtr<PromiseTask> task)
    2014             : {
    2015             :     // Execute synchronously if there are no helper threads.
    2016           0 :     if (!CanUseExtraThreads())
    2017           0 :         return task->executeAndFinish(cx);
    2018             : 
    2019             :     // If we fail to start, by interface contract, it is because the JSContext
    2020             :     // is in the process of shutting down. Since promise handlers are not
    2021             :     // necessarily run while shutting down *anyway*, we simply ignore the error.
    2022             :     // This is symmetric with the handling of errors in finishAsyncTaskCallback
    2023             :     // which, since it is off the JSContext's owner thread, cannot report an
    2024             :     // error anyway.
    2025           0 :     if (!cx->runtime()->startAsyncTaskCallback(cx, task.get())) {
    2026           0 :         MOZ_ASSERT(!cx->isExceptionPending());
    2027           0 :         return true;
    2028             :     }
    2029             : 
    2030             :     // Per interface contract, after startAsyncTaskCallback succeeds,
    2031             :     // finishAsyncTaskCallback *must* be called on all paths.
    2032             : 
    2033           0 :     AutoLockHelperThreadState lock;
    2034             : 
    2035           0 :     if (!HelperThreadState().promiseTasks(lock).append(task.get())) {
    2036           0 :         Unused << cx->runtime()->finishAsyncTaskCallback(task.get());
    2037           0 :         ReportOutOfMemory(cx);
    2038           0 :         return false;
    2039             :     }
    2040             : 
    2041           0 :     Unused << task.release();
    2042             : 
    2043           0 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
    2044           0 :     return true;
    2045             : }
    2046             : 
    2047             : void
    2048          22 : GlobalHelperThreadState::trace(JSTracer* trc)
    2049             : {
    2050          44 :     AutoLockHelperThreadState lock;
    2051          22 :     for (auto builder : ionWorklist(lock))
    2052           0 :         builder->trace(trc);
    2053          22 :     for (auto builder : ionFinishedList(lock))
    2054           0 :         builder->trace(trc);
    2055             : 
    2056          22 :     if (HelperThreadState().threads) {
    2057         286 :         for (auto& helper : *HelperThreadState().threads) {
    2058         264 :             if (auto builder = helper.ionBuilder())
    2059           0 :                 builder->trace(trc);
    2060             :         }
    2061             :     }
    2062             : 
    2063         197 :     for (ZoneGroupsIter group(trc->runtime()); !group.done(); group.next()) {
    2064         175 :         jit::IonBuilder* builder = group->ionLazyLinkList().getFirst();
    2065         179 :         while (builder) {
    2066           2 :             builder->trace(trc);
    2067           2 :             builder = builder->getNext();
    2068             :         }
    2069             :     }
    2070             : 
    2071          22 :     for (auto parseTask : parseWorklist_)
    2072           0 :         parseTask->trace(trc);
    2073          27 :     for (auto parseTask : parseFinishedList_)
    2074           5 :         parseTask->trace(trc);
    2075          22 :     for (auto parseTask : parseWaitingOnGC_)
    2076           0 :         parseTask->trace(trc);
    2077          22 : }
    2078             : 
    2079             : void
    2080           0 : HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
    2081             : {
    2082           0 :     MOZ_ASSERT(HelperThreadState().canStartGCHelperTask(locked));
    2083           0 :     MOZ_ASSERT(idle());
    2084             : 
    2085           0 :     currentTask.emplace(HelperThreadState().gcHelperWorklist(locked).popCopy());
    2086           0 :     GCHelperState* task = gcHelperTask();
    2087             : 
    2088           0 :     AutoSetContextRuntime ascr(task->runtime());
    2089             : 
    2090             :     {
    2091           0 :         AutoUnlockHelperThreadState unlock(locked);
    2092           0 :         task->work();
    2093             :     }
    2094             : 
    2095           0 :     currentTask.reset();
    2096           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    2097           0 : }
    2098             : 
    2099             : void
    2100          36 : JSContext::setHelperThread(HelperThread* thread)
    2101             : {
    2102          36 :     helperThread_ = thread;
    2103          36 : }
    2104             : 
    2105             : void
    2106          36 : HelperThread::threadLoop()
    2107             : {
    2108          36 :     MOZ_ASSERT(CanUseExtraThreads());
    2109             : 
    2110          35 :     JS::AutoSuppressGCAnalysis nogc;
    2111          36 :     AutoLockHelperThreadState lock;
    2112             : 
    2113          36 :     JSContext cx(nullptr, JS::ContextOptions());
    2114             :     {
    2115          72 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    2116          36 :         if (!cx.init(ContextKind::Background))
    2117           0 :             oomUnsafe.crash("HelperThread cx.init()");
    2118             :     }
    2119          36 :     cx.setHelperThread(this);
    2120          36 :     JS_SetNativeStackQuota(&cx, HELPER_STACK_QUOTA);
    2121             : 
    2122             :     while (true) {
    2123          90 :         MOZ_ASSERT(idle());
    2124             : 
    2125             :         // Block until a task is available. Save the value of whether we are
    2126             :         // going to do an Ion compile, in case the value returned by the method
    2127             :         // changes.
    2128          90 :         bool ionCompile = false;
    2129             :         while (true) {
    2130         200 :             if (terminate)
    2131           0 :                 return;
    2132         427 :             if ((ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority(lock)) ||
    2133         274 :                 HelperThreadState().canStartWasmCompile(lock) ||
    2134         274 :                 HelperThreadState().canStartPromiseTask(lock) ||
    2135         258 :                 HelperThreadState().canStartParseTask(lock) ||
    2136         242 :                 HelperThreadState().canStartCompressionTask(lock) ||
    2137         242 :                 HelperThreadState().canStartGCHelperTask(lock) ||
    2138         364 :                 HelperThreadState().canStartGCParallelTask(lock) ||
    2139          98 :                 HelperThreadState().canStartIonFreeTask(lock))
    2140             :             {
    2141          55 :                 break;
    2142             :             }
    2143          90 :             HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
    2144             :         }
    2145             : 
    2146          55 :         if (HelperThreadState().canStartGCParallelTask(lock)) {
    2147          23 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCPARALLEL);
    2148          23 :             handleGCParallelWorkload(lock);
    2149          32 :         } else if (HelperThreadState().canStartGCHelperTask(lock)) {
    2150           0 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCHELPER);
    2151           0 :             handleGCHelperWorkload(lock);
    2152          32 :         } else if (ionCompile) {
    2153           8 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
    2154           8 :             handleIonWorkload(lock);
    2155          24 :         } else if (HelperThreadState().canStartWasmCompile(lock)) {
    2156           0 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_WASM);
    2157           0 :             handleWasmWorkload(lock);
    2158          24 :         } else if (HelperThreadState().canStartPromiseTask(lock)) {
    2159           0 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_PROMISE_TASK);
    2160           0 :             handlePromiseTaskWorkload(lock);
    2161          24 :         } else if (HelperThreadState().canStartParseTask(lock)) {
    2162          16 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_PARSE);
    2163          16 :             handleParseWorkload(lock);
    2164           8 :         } else if (HelperThreadState().canStartCompressionTask(lock)) {
    2165           0 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_COMPRESS);
    2166           0 :             handleCompressionWorkload(lock);
    2167           8 :         } else if (HelperThreadState().canStartIonFreeTask(lock)) {
    2168           8 :             js::oom::SetThreadType(js::oom::THREAD_TYPE_ION_FREE);
    2169           8 :             handleIonFreeWorkload(lock);
    2170             :         } else {
    2171           0 :             MOZ_CRASH("No task to perform");
    2172             :         }
    2173          54 :     }
    2174             : }

Generated by: LCOV version 1.13