LCOV - code coverage report
Current view: top level - js/src/vm - HelperThreads.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 254 1020 24.9 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
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/Maybe.h"
      10             : #include "mozilla/ScopeExit.h"
      11             : #include "mozilla/Unused.h"
      12             : 
      13             : #include "builtin/Promise.h"
      14             : #include "frontend/BytecodeCompiler.h"
      15             : #include "gc/GCInternals.h"
      16             : #include "jit/IonBuilder.h"
      17             : #include "js/Utility.h"
      18             : #include "threading/CpuCount.h"
      19             : #include "util/NativeStack.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             : #include "wasm/WasmGenerator.h"
      27             : 
      28             : #include "gc/PrivateIterators-inl.h"
      29             : #include "vm/JSContext-inl.h"
      30             : #include "vm/JSObject-inl.h"
      31             : #include "vm/JSScript-inl.h"
      32             : #include "vm/NativeObject-inl.h"
      33             : #include "vm/Realm-inl.h"
      34             : 
      35             : using namespace js;
      36             : 
      37             : using mozilla::Maybe;
      38             : using mozilla::Unused;
      39             : using mozilla::TimeDuration;
      40             : using mozilla::TimeStamp;
      41             : 
      42             : namespace js {
      43             : 
      44             : GlobalHelperThreadState* gHelperThreadState = nullptr;
      45             : 
      46             : } // namespace js
      47             : 
      48             : bool
      49           0 : js::CreateHelperThreadsState()
      50             : {
      51           0 :     MOZ_ASSERT(!gHelperThreadState);
      52           0 :     gHelperThreadState = js_new<GlobalHelperThreadState>();
      53           0 :     return gHelperThreadState != nullptr;
      54             : }
      55             : 
      56             : void
      57           0 : js::DestroyHelperThreadsState()
      58             : {
      59           0 :     MOZ_ASSERT(gHelperThreadState);
      60           0 :     gHelperThreadState->finish();
      61           0 :     js_delete(gHelperThreadState);
      62           0 :     gHelperThreadState = nullptr;
      63           0 : }
      64             : 
      65             : bool
      66           0 : js::EnsureHelperThreadsInitialized()
      67             : {
      68           0 :     MOZ_ASSERT(gHelperThreadState);
      69           0 :     return gHelperThreadState->ensureInitialized();
      70             : }
      71             : 
      72             : static size_t
      73             : ClampDefaultCPUCount(size_t cpuCount)
      74             : {
      75             :     // It's extremely rare for SpiderMonkey to have more than a few cores worth
      76             :     // of work. At higher core counts, performance can even decrease due to NUMA
      77             :     // (and SpiderMonkey's lack of NUMA-awareness), contention, and general lack
      78             :     // of optimization for high core counts. So to avoid wasting thread stack
      79             :     // resources (and cluttering gdb and core dumps), clamp to 8 cores for now.
      80           0 :     return Min<size_t>(cpuCount, 8);
      81             : }
      82             : 
      83             : static size_t
      84             : ThreadCountForCPUCount(size_t cpuCount)
      85             : {
      86             :     // We need at least two threads for tier-2 wasm compilations, because
      87             :     // there's a master task that holds a thread while other threads do the
      88             :     // compilation.
      89           0 :     return Max<size_t>(cpuCount, 2);
      90             : }
      91             : 
      92             : void
      93           0 : js::SetFakeCPUCount(size_t count)
      94             : {
      95             :     // This must be called before the threads have been initialized.
      96           0 :     MOZ_ASSERT(!HelperThreadState().threads);
      97             : 
      98           0 :     HelperThreadState().cpuCount = count;
      99           0 :     HelperThreadState().threadCount = ThreadCountForCPUCount(count);
     100           0 : }
     101             : 
     102             : void
     103           0 : JS::SetProfilingThreadCallbacks(JS::RegisterThreadCallback registerThread,
     104             :                                 JS::UnregisterThreadCallback unregisterThread)
     105             : {
     106           2 :     HelperThreadState().registerThread = registerThread;
     107           0 :     HelperThreadState().unregisterThread = unregisterThread;
     108           1 : }
     109             : 
     110             : bool
     111           0 : js::StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode)
     112             : {
     113           0 :     AutoLockHelperThreadState lock;
     114             : 
     115           0 :     if (!HelperThreadState().wasmWorklist(lock, mode).pushBack(task))
     116             :         return false;
     117             : 
     118           0 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     119           0 :     return true;
     120             : }
     121             : 
     122             : void
     123           0 : js::StartOffThreadWasmTier2Generator(wasm::UniqueTier2GeneratorTask task)
     124             : {
     125           0 :     MOZ_ASSERT(CanUseExtraThreads());
     126             : 
     127           0 :     AutoLockHelperThreadState lock;
     128             : 
     129           0 :     if (!HelperThreadState().wasmTier2GeneratorWorklist(lock).append(task.get()))
     130           0 :         return;
     131             : 
     132           0 :     Unused << task.release();
     133             : 
     134           0 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     135             : }
     136             : 
     137             : static void
     138           0 : CancelOffThreadWasmTier2GeneratorLocked(AutoLockHelperThreadState& lock)
     139             : {
     140           0 :     if (!HelperThreadState().threads)
     141             :         return;
     142             : 
     143             :     // Remove pending tasks from the tier2 generator worklist and cancel and
     144             :     // delete them.
     145             :     {
     146             :         wasm::Tier2GeneratorTaskPtrVector& worklist =
     147           0 :             HelperThreadState().wasmTier2GeneratorWorklist(lock);
     148           0 :         for (size_t i = 0; i < worklist.length(); i++) {
     149           0 :             wasm::Tier2GeneratorTask* task = worklist[i];
     150           0 :             HelperThreadState().remove(worklist, &i);
     151           0 :             js_delete(task);
     152             :         }
     153             :     }
     154             : 
     155             :     // There is at most one running Tier2Generator task and we assume that
     156             :     // below.
     157             :     static_assert(GlobalHelperThreadState::MaxTier2GeneratorTasks == 1,
     158             :                   "code must be generalized");
     159             : 
     160             :     // If there is a running Tier2 generator task, shut it down in a predictable
     161             :     // way.  The task will be deleted by the normal deletion logic.
     162           0 :     for (auto& helper : *HelperThreadState().threads) {
     163           0 :         if (helper.wasmTier2GeneratorTask()) {
     164             :             // Set a flag that causes compilation to shortcut itself.
     165           0 :             helper.wasmTier2GeneratorTask()->cancel();
     166             : 
     167             :             // Wait for the generator task to finish.  This avoids a shutdown race where
     168             :             // the shutdown code is trying to shut down helper threads and the ongoing
     169             :             // tier2 compilation is trying to finish, which requires it to have access
     170             :             // to helper threads.
     171           0 :             uint32_t oldFinishedCount = HelperThreadState().wasmTier2GeneratorsFinished(lock);
     172           0 :             while (HelperThreadState().wasmTier2GeneratorsFinished(lock) == oldFinishedCount)
     173           0 :                 HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     174             : 
     175             :             // At most one of these tasks.
     176             :             break;
     177             :         }
     178             :     }
     179             : }
     180             : 
     181             : void
     182           0 : js::CancelOffThreadWasmTier2Generator()
     183             : {
     184           0 :     AutoLockHelperThreadState lock;
     185           0 :     CancelOffThreadWasmTier2GeneratorLocked(lock);
     186           0 : }
     187             : 
     188             : bool
     189          50 : js::StartOffThreadIonCompile(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
     190             : {
     191           0 :     if (!HelperThreadState().ionWorklist(lock).append(builder))
     192             :         return false;
     193             : 
     194         100 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     195           0 :     return true;
     196             : }
     197             : 
     198             : bool
     199           0 : js::StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
     200             : {
     201          49 :     MOZ_ASSERT(CanUseExtraThreads());
     202             : 
     203          98 :     if (!HelperThreadState().ionFreeList(lock).append(builder))
     204             :         return false;
     205             : 
     206          98 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     207          49 :     return true;
     208             : }
     209             : 
     210             : /*
     211             :  * Move an IonBuilder for which compilation has either finished, failed, or
     212             :  * been cancelled into the global finished compilation list. All off thread
     213             :  * compilations which are started must eventually be finished.
     214             :  */
     215             : static void
     216          50 : FinishOffThreadIonCompile(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
     217             : {
     218         100 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     219         100 :     if (!HelperThreadState().ionFinishedList(lock).append(builder))
     220           0 :         oomUnsafe.crash("FinishOffThreadIonCompile");
     221           0 :     builder->script()->runtimeFromAnyThread()->jitRuntime()->numFinishedBuildersRef(lock)++;
     222           0 : }
     223             : 
     224             : static JSRuntime*
     225             : GetSelectorRuntime(const CompilationSelector& selector)
     226             : {
     227             :     struct Matcher
     228             :     {
     229         376 :         JSRuntime* match(JSScript* script)    { return script->runtimeFromMainThread(); }
     230           0 :         JSRuntime* match(Realm* realm)        { return realm->runtimeFromMainThread(); }
     231           0 :         JSRuntime* match(Zone* zone)          { return zone->runtimeFromMainThread(); }
     232             :         JSRuntime* match(ZonesInState zbs)    { return zbs.runtime; }
     233             :         JSRuntime* match(JSRuntime* runtime)  { return runtime; }
     234             :         JSRuntime* match(AllCompilations all) { return nullptr; }
     235             :         JSRuntime* match(CompilationsUsingNursery cun) { return cun.runtime; }
     236             :     };
     237             : 
     238           0 :     return selector.match(Matcher());
     239             : }
     240             : 
     241             : static bool
     242             : JitDataStructuresExist(const CompilationSelector& selector)
     243             : {
     244             :     struct Matcher
     245             :     {
     246         752 :         bool match(JSScript* script)    { return !!script->realm()->jitRealm(); }
     247           0 :         bool match(Realm* realm)        { return !!realm->jitRealm(); }
     248           0 :         bool match(Zone* zone)          { return !!zone->jitZone(); }
     249           0 :         bool match(ZonesInState zbs)    { return zbs.runtime->hasJitRuntime(); }
     250           0 :         bool match(JSRuntime* runtime)  { return runtime->hasJitRuntime(); }
     251             :         bool match(AllCompilations all) { return true; }
     252           4 :         bool match(CompilationsUsingNursery cun) { return cun.runtime->hasJitRuntime(); }
     253             :     };
     254             : 
     255         756 :     return selector.match(Matcher());
     256             : }
     257             : 
     258             : static bool
     259             : IonBuilderMatches(const CompilationSelector& selector, jit::IonBuilder* builder)
     260             : {
     261             :     struct BuilderMatches
     262             :     {
     263             :         jit::IonBuilder* builder_;
     264             : 
     265         401 :         bool match(JSScript* script)    { return script == builder_->script(); }
     266           0 :         bool match(Realm* realm)        { return realm == builder_->script()->realm(); }
     267           0 :         bool match(Zone* zone)          { return zone == builder_->script()->zone(); }
     268           0 :         bool match(JSRuntime* runtime)  { return runtime == builder_->script()->runtimeFromAnyThread(); }
     269             :         bool match(AllCompilations all) { return true; }
     270           0 :         bool match(ZonesInState zbs)    {
     271           0 :             return zbs.runtime == builder_->script()->runtimeFromAnyThread() &&
     272           0 :                    zbs.state == builder_->script()->zoneFromAnyThread()->gcState();
     273             :         }
     274             :         bool match(CompilationsUsingNursery cun) {
     275           9 :             return cun.runtime == builder_->script()->runtimeFromAnyThread() &&
     276           0 :                    !builder_->safeForMinorGC();
     277             :         }
     278             :     };
     279             : 
     280         808 :     return selector.match(BuilderMatches{builder});
     281             : }
     282             : 
     283             : static void
     284           0 : CancelOffThreadIonCompileLocked(const CompilationSelector& selector, bool discardLazyLinkList,
     285             :                                 AutoLockHelperThreadState& lock)
     286             : {
     287           0 :     if (!HelperThreadState().threads)
     288             :         return;
     289             : 
     290             :     /* Cancel any pending entries for which processing hasn't started. */
     291         756 :     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     292         378 :     for (size_t i = 0; i < worklist.length(); i++) {
     293           0 :         jit::IonBuilder* builder = worklist[i];
     294           0 :         if (IonBuilderMatches(selector, builder)) {
     295           0 :             FinishOffThreadIonCompile(builder, lock);
     296           0 :             HelperThreadState().remove(worklist, &i);
     297             :         }
     298             :     }
     299             : 
     300             :     /* Wait for in progress entries to finish up. */
     301             :     bool cancelled;
     302         380 :     do {
     303         380 :         cancelled = false;
     304           0 :         for (auto& helper : *HelperThreadState().threads) {
     305           0 :             if (helper.ionBuilder() &&
     306         250 :                 IonBuilderMatches(selector, helper.ionBuilder()))
     307             :             {
     308           4 :                 helper.ionBuilder()->cancel();
     309           0 :                 cancelled = true;
     310             :             }
     311             :         }
     312           0 :         if (cancelled)
     313           0 :             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     314             :     } while (cancelled);
     315             : 
     316             :     /* Cancel code generation for any completed entries. */
     317         756 :     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     318         381 :     for (size_t i = 0; i < finished.length(); i++) {
     319           3 :         jit::IonBuilder* builder = finished[i];
     320           3 :         if (IonBuilderMatches(selector, builder)) {
     321           0 :             JSRuntime* rt = builder->script()->runtimeFromAnyThread();
     322           0 :             rt->jitRuntime()->numFinishedBuildersRef(lock)--;
     323           0 :             jit::FinishOffThreadBuilder(rt, builder, lock);
     324           0 :             HelperThreadState().remove(finished, &i);
     325             :         }
     326             :     }
     327             : 
     328             :     /* Cancel lazy linking for pending builders (attached to the ionScript). */
     329         378 :     if (discardLazyLinkList) {
     330         756 :         MOZ_ASSERT(!selector.is<AllCompilations>());
     331         378 :         JSRuntime* runtime = GetSelectorRuntime(selector);
     332         378 :         jit::IonBuilder* builder = runtime->jitRuntime()->ionLazyLinkList(runtime).getFirst();
     333         654 :         while (builder) {
     334         552 :             jit::IonBuilder* next = builder->getNext();
     335           0 :             if (IonBuilderMatches(selector, builder))
     336           3 :                 jit::FinishOffThreadBuilder(runtime, builder, lock);
     337             :             builder = next;
     338             :         }
     339             :     }
     340             : }
     341             : 
     342             : void
     343         378 : js::CancelOffThreadIonCompile(const CompilationSelector& selector, bool discardLazyLinkList)
     344             : {
     345         378 :     if (!JitDataStructuresExist(selector))
     346           0 :         return;
     347             : 
     348           0 :     AutoLockHelperThreadState lock;
     349         378 :     CancelOffThreadIonCompileLocked(selector, discardLazyLinkList, lock);
     350             : }
     351             : 
     352             : #ifdef DEBUG
     353             : bool
     354           0 : js::HasOffThreadIonCompile(Realm* realm)
     355             : {
     356           0 :     AutoLockHelperThreadState lock;
     357             : 
     358           0 :     if (!HelperThreadState().threads)
     359             :         return false;
     360             : 
     361           0 :     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     362           0 :     for (size_t i = 0; i < worklist.length(); i++) {
     363           0 :         jit::IonBuilder* builder = worklist[i];
     364           0 :         if (builder->script()->realm() == realm)
     365             :             return true;
     366             :     }
     367             : 
     368           0 :     for (auto& helper : *HelperThreadState().threads) {
     369           0 :         if (helper.ionBuilder() && helper.ionBuilder()->script()->realm() == realm)
     370             :             return true;
     371             :     }
     372             : 
     373           0 :     GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     374           0 :     for (size_t i = 0; i < finished.length(); i++) {
     375           0 :         jit::IonBuilder* builder = finished[i];
     376           0 :         if (builder->script()->realm() == realm)
     377             :             return true;
     378             :     }
     379             : 
     380           0 :     JSRuntime* rt = realm->runtimeFromMainThread();
     381           0 :     jit::IonBuilder* builder = rt->jitRuntime()->ionLazyLinkList(rt).getFirst();
     382           0 :     while (builder) {
     383           0 :         if (builder->script()->realm() == realm)
     384             :             return true;
     385           0 :         builder = builder->getNext();
     386             :     }
     387             : 
     388             :     return false;
     389             : }
     390             : #endif
     391             : 
     392             : static const JSClassOps parseTaskGlobalClassOps = {
     393             :     nullptr, nullptr, nullptr, nullptr,
     394             :     nullptr, nullptr, nullptr, nullptr,
     395             :     nullptr, nullptr,
     396             :     JS_GlobalObjectTraceHook
     397             : };
     398             : 
     399             : static const JSClass parseTaskGlobalClass = {
     400             :     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     401             :     &parseTaskGlobalClassOps
     402             : };
     403             : 
     404           0 : ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx,
     405           5 :                      JS::OffThreadCompileCallback callback, void* callbackData)
     406             :   : kind(kind),
     407             :     options(cx),
     408             :     alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     409             :     parseGlobal(nullptr),
     410             :     callback(callback), callbackData(callbackData),
     411             :     scripts(cx), sourceObjects(cx),
     412          25 :     overRecursed(false), outOfMemory(false)
     413             : {
     414          15 :     MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
     415          15 :     MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
     416           0 : }
     417             : 
     418             : bool
     419           0 : ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options, JSObject* global)
     420             : {
     421           0 :     if (!this->options.copy(cx, options))
     422             :         return false;
     423             : 
     424           0 :     parseGlobal = global;
     425           0 :     return true;
     426             : }
     427             : 
     428             : void
     429           0 : ParseTask::activate(JSRuntime* rt)
     430             : {
     431           0 :     rt->setUsedByHelperThread(parseGlobal->zone());
     432           1 : }
     433             : 
     434             : bool
     435           5 : ParseTask::finish(JSContext* cx)
     436             : {
     437          15 :     for (auto& sourceObject : sourceObjects) {
     438          10 :         RootedScriptSourceObject sso(cx, sourceObject);
     439          10 :         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
     440           0 :             return false;
     441          15 :         if (!sso->source()->tryCompressOffThread(cx))
     442             :             return false;
     443             :     }
     444             : 
     445             :     return true;
     446             : }
     447             : 
     448          20 : ParseTask::~ParseTask()
     449             : {
     450           5 :     for (size_t i = 0; i < errors.length(); i++)
     451           0 :         js_delete(errors[i]);
     452           0 : }
     453             : 
     454             : void
     455           0 : ParseTask::trace(JSTracer* trc)
     456             : {
     457           0 :     if (parseGlobal->runtimeFromAnyThread() != trc->runtime())
     458             :         return;
     459             : 
     460           0 :     Zone* zone = MaybeForwarded(parseGlobal)->zoneFromAnyThread();
     461           0 :     if (zone->usedByHelperThread()) {
     462           0 :         MOZ_ASSERT(!zone->isCollecting());
     463             :         return;
     464             :     }
     465             : 
     466           0 :     TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
     467           0 :     scripts.trace(trc);
     468           0 :     sourceObjects.trace(trc);
     469             : }
     470             : 
     471             : size_t
     472           0 : ParseTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     473             : {
     474           0 :     return options.sizeOfExcludingThis(mallocSizeOf) +
     475           0 :            alloc.sizeOfExcludingThis(mallocSizeOf) +
     476           0 :            errors.sizeOfExcludingThis(mallocSizeOf);
     477             : }
     478             : 
     479           0 : ScriptParseTask::ScriptParseTask(JSContext* cx, const char16_t* chars, size_t length,
     480           0 :                                  JS::OffThreadCompileCallback callback, void* callbackData)
     481             :   : ParseTask(ParseTaskKind::Script, cx, callback, callbackData),
     482          10 :     data(TwoByteChars(chars, length))
     483           0 : {}
     484             : 
     485             : void
     486           5 : ScriptParseTask::parse(JSContext* cx)
     487             : {
     488           0 :     SourceBufferHolder srcBuf(data.begin().get(), data.length(), SourceBufferHolder::NoOwnership);
     489           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     490             : 
     491           0 :     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
     492             : 
     493           5 :     JSScript* script = frontend::CompileGlobalScript(cx, alloc, scopeKind,
     494             :                                                      options, srcBuf,
     495           0 :                                                      /* sourceObjectOut = */ &sourceObject.get());
     496           5 :     if (script)
     497           0 :         scripts.infallibleAppend(script);
     498           1 :     if (sourceObject)
     499           5 :         sourceObjects.infallibleAppend(sourceObject);
     500           5 : }
     501             : 
     502           0 : ModuleParseTask::ModuleParseTask(JSContext* cx, const char16_t* chars, size_t length,
     503           0 :                                  JS::OffThreadCompileCallback callback, void* callbackData)
     504             :   : ParseTask(ParseTaskKind::Module, cx, callback, callbackData),
     505           0 :     data(TwoByteChars(chars, length))
     506           0 : {}
     507             : 
     508             : void
     509           0 : ModuleParseTask::parse(JSContext* cx)
     510             : {
     511           0 :     SourceBufferHolder srcBuf(data.begin().get(), data.length(), SourceBufferHolder::NoOwnership);
     512           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     513             : 
     514           0 :     ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject.get());
     515           0 :     if (module) {
     516           0 :         scripts.infallibleAppend(module->script());
     517           0 :         if (sourceObject)
     518           0 :             sourceObjects.infallibleAppend(sourceObject);
     519             :     }
     520           0 : }
     521             : 
     522           0 : ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, const JS::TranscodeRange& range,
     523           0 :                                    JS::OffThreadCompileCallback callback, void* callbackData)
     524             :   : ParseTask(ParseTaskKind::ScriptDecode, cx, callback, callbackData),
     525           0 :     range(range)
     526           0 : {}
     527             : 
     528             : void
     529           0 : ScriptDecodeTask::parse(JSContext* cx)
     530             : {
     531           0 :     RootedScript resultScript(cx);
     532           0 :     Rooted<ScriptSourceObject*> sourceObject(cx);
     533             : 
     534           0 :     XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject.get(),
     535           0 :                                 range);
     536           0 :     XDRResult res = decoder.codeScript(&resultScript);
     537           0 :     MOZ_ASSERT(bool(resultScript) == res.isOk());
     538           0 :     if (res.isOk()) {
     539           0 :         scripts.infallibleAppend(resultScript);
     540           0 :         if (sourceObject)
     541           0 :             sourceObjects.infallibleAppend(sourceObject);
     542             :     }
     543           0 : }
     544             : 
     545             : #if defined(JS_BUILD_BINAST)
     546             : 
     547           0 : BinASTDecodeTask::BinASTDecodeTask(JSContext* cx, const uint8_t* buf, size_t length,
     548           0 :                                    JS::OffThreadCompileCallback callback, void* callbackData)
     549             :   : ParseTask(ParseTaskKind::BinAST, cx, callback, callbackData),
     550           0 :     data(buf, length)
     551           0 : {}
     552             : 
     553             : void
     554           0 : BinASTDecodeTask::parse(JSContext* cx)
     555             : {
     556           0 :     RootedScriptSourceObject sourceObject(cx);
     557             : 
     558           0 :     JSScript* script = frontend::CompileGlobalBinASTScript(cx, alloc, options,
     559           0 :                                                            data.begin().get(), data.length(),
     560           0 :                                                            &sourceObject.get());
     561           0 :     if (script) {
     562           0 :         scripts.infallibleAppend(script);
     563           0 :         if (sourceObject)
     564           0 :             sourceObjects.infallibleAppend(sourceObject);
     565             :     }
     566           0 : }
     567             : 
     568             : #endif /* JS_BUILD_BINAST */
     569             : 
     570           0 : MultiScriptsDecodeTask::MultiScriptsDecodeTask(JSContext* cx, JS::TranscodeSources& sources,
     571             :                                                JS::OffThreadCompileCallback callback,
     572           0 :                                                void* callbackData)
     573             :   : ParseTask(ParseTaskKind::MultiScriptsDecode, cx, callback, callbackData),
     574           0 :     sources(&sources)
     575           0 : {}
     576             : 
     577             : void
     578           0 : MultiScriptsDecodeTask::parse(JSContext* cx)
     579             : {
     580           0 :     if (!scripts.reserve(sources->length()) ||
     581           0 :         !sourceObjects.reserve(sources->length()))
     582             :     {
     583             :         return;
     584             :     }
     585             : 
     586           0 :     for (auto& source : *sources) {
     587           0 :         CompileOptions opts(cx, options);
     588           0 :         opts.setFileAndLine(source.filename, source.lineno);
     589             : 
     590           0 :         RootedScript resultScript(cx);
     591           0 :         Rooted<ScriptSourceObject*> sourceObject(cx);
     592             : 
     593           0 :         XDROffThreadDecoder decoder(cx, alloc, &opts, &sourceObject.get(), source.range);
     594           0 :         XDRResult res = decoder.codeScript(&resultScript);
     595           0 :         MOZ_ASSERT(bool(resultScript) == res.isOk());
     596             : 
     597           0 :         if (res.isErr())
     598             :             break;
     599           0 :         MOZ_ASSERT(resultScript);
     600           0 :         scripts.infallibleAppend(resultScript);
     601           0 :         sourceObjects.infallibleAppend(sourceObject);
     602             :     }
     603             : }
     604             : 
     605             : void
     606           0 : js::CancelOffThreadParses(JSRuntime* rt)
     607             : {
     608           0 :     AutoLockHelperThreadState lock;
     609             : 
     610           0 :     if (!HelperThreadState().threads)
     611           0 :         return;
     612             : 
     613             : #ifdef DEBUG
     614             :     GlobalHelperThreadState::ParseTaskVector& waitingOnGC =
     615           0 :         HelperThreadState().parseWaitingOnGC(lock);
     616           0 :     for (size_t i = 0; i < waitingOnGC.length(); i++)
     617           0 :         MOZ_ASSERT(!waitingOnGC[i]->runtimeMatches(rt));
     618             : #endif
     619             : 
     620             :     // Instead of forcibly canceling pending parse tasks, just wait for all scheduled
     621             :     // and in progress ones to complete. Otherwise the final GC may not collect
     622             :     // everything due to zones being used off thread.
     623             :     while (true) {
     624           0 :         bool pending = false;
     625           0 :         GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist(lock);
     626           0 :         for (size_t i = 0; i < worklist.length(); i++) {
     627           0 :             ParseTask* task = worklist[i];
     628           0 :             if (task->runtimeMatches(rt))
     629           0 :                 pending = true;
     630             :         }
     631           0 :         if (!pending) {
     632           0 :             bool inProgress = false;
     633           0 :             for (auto& thread : *HelperThreadState().threads) {
     634           0 :                 ParseTask* task = thread.parseTask();
     635           0 :                 if (task && task->runtimeMatches(rt))
     636           0 :                     inProgress = true;
     637             :             }
     638           0 :             if (!inProgress)
     639             :                 break;
     640             :         }
     641           0 :         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     642             :     }
     643             : 
     644             :     // Clean up any parse tasks which haven't been finished by the main thread.
     645           0 :     auto& finished = HelperThreadState().parseFinishedList(lock);
     646             :     while (true) {
     647           0 :         bool found = false;
     648             :         ParseTask* next;
     649             :         ParseTask* task = finished.getFirst();
     650           0 :         while (task) {
     651           0 :             next = task->getNext();
     652           0 :             if (task->runtimeMatches(rt)) {
     653           0 :                 found = true;
     654           0 :                 task->remove();
     655           0 :                 HelperThreadState().destroyParseTask(rt, task);
     656             :             }
     657             :             task = next;
     658             :         }
     659           0 :         if (!found)
     660             :             break;
     661             :     }
     662             : 
     663             : #ifdef DEBUG
     664           0 :     GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist(lock);
     665           0 :     for (size_t i = 0; i < worklist.length(); i++) {
     666           0 :         ParseTask* task = worklist[i];
     667           0 :         MOZ_ASSERT(!task->runtimeMatches(rt));
     668             :     }
     669             : #endif
     670             : }
     671             : 
     672             : bool
     673          10 : js::OffThreadParsingMustWaitForGC(JSRuntime* rt)
     674             : {
     675             :     // Off thread parsing can't occur during incremental collections on the
     676             :     // atoms zone, to avoid triggering barriers. (Outside the atoms zone, the
     677             :     // compilation will use a new zone that is never collected.) If an
     678             :     // atoms-zone GC is in progress, hold off on executing the parse task until
     679             :     // the atoms-zone GC completes (see EnqueuePendingParseTasksAfterGC).
     680          15 :     return rt->activeGCInAtomsZone();
     681             : }
     682             : 
     683             : static bool
     684          15 : EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
     685             : {
     686          15 :     if (!GlobalObject::ensureConstructor(cx, global, key))
     687             :         return false;
     688             : 
     689           0 :     MOZ_ASSERT(global->getPrototype(key).toObject().isDelegate(),
     690             :                "standard class prototype wasn't a delegate from birth");
     691             :     return true;
     692             : }
     693             : 
     694             : // Initialize all classes potentially created during parsing for use in parser
     695             : // data structures, template objects, &c.
     696             : static bool
     697           5 : EnsureParserCreatedClasses(JSContext* cx, ParseTaskKind kind)
     698             : {
     699           0 :     Handle<GlobalObject*> global = cx->global();
     700             : 
     701           5 :     if (!EnsureConstructor(cx, global, JSProto_Function))
     702             :         return false; // needed by functions, also adds object literals' proto
     703             : 
     704           5 :     if (!EnsureConstructor(cx, global, JSProto_Array))
     705             :         return false; // needed by array literals
     706             : 
     707           5 :     if (!EnsureConstructor(cx, global, JSProto_RegExp))
     708             :         return false; // needed by regular expression literals
     709             : 
     710           5 :     if (!GlobalObject::initGenerators(cx, global))
     711             :         return false; // needed by function*() {}
     712             : 
     713           5 :     if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global))
     714             :         return false;
     715             : 
     716           5 :     return true;
     717             : }
     718             : 
     719             : class MOZ_RAII AutoSetCreatedForHelperThread
     720             : {
     721             :     Zone* zone;
     722             : 
     723             :   public:
     724             :     explicit AutoSetCreatedForHelperThread(JSObject* global)
     725          10 :       : zone(global->zone())
     726             :     {
     727           0 :         zone->setCreatedForHelperThread();
     728             :     }
     729             : 
     730             :     void forget() {
     731           5 :         zone = nullptr;
     732             :     }
     733             : 
     734             :     ~AutoSetCreatedForHelperThread() {
     735           0 :         if (zone)
     736           0 :             zone->clearUsedByHelperThread();
     737             :     }
     738             : };
     739             : 
     740             : static JSObject*
     741           5 : CreateGlobalForOffThreadParse(JSContext* cx, const gc::AutoSuppressGC& nogc)
     742             : {
     743           0 :     JS::Realm* currentRealm = cx->realm();
     744             : 
     745             :     JS::RealmOptions realmOptions(currentRealm->creationOptions(),
     746          10 :                                   currentRealm->behaviors());
     747             : 
     748           5 :     auto& creationOptions = realmOptions.creationOptions();
     749             : 
     750           0 :     creationOptions.setInvisibleToDebugger(true)
     751           0 :                    .setMergeable(true)
     752           5 :                    .setNewCompartmentAndZone();
     753             : 
     754             :     // Don't falsely inherit the host's global trace hook.
     755           5 :     creationOptions.setTrace(nullptr);
     756             : 
     757           5 :     JSObject* obj = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
     758           0 :                                        JS::DontFireOnNewGlobalHook, realmOptions);
     759           5 :     if (!obj)
     760             :         return nullptr;
     761             : 
     762           0 :     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     763             : 
     764           0 :     JS::SetRealmPrincipals(global->realm(), currentRealm->principals());
     765             : 
     766           0 :     return global;
     767             : }
     768             : 
     769             : static bool
     770           0 : QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
     771             : {
     772           0 :     AutoLockHelperThreadState lock;
     773             : 
     774          10 :     bool mustWait = OffThreadParsingMustWaitForGC(cx->runtime());
     775             : 
     776           0 :     auto& queue = mustWait ? HelperThreadState().parseWaitingOnGC(lock)
     777           0 :                            : HelperThreadState().parseWorklist(lock);
     778           5 :     if (!queue.append(task)) {
     779           0 :         ReportOutOfMemory(cx);
     780           0 :         return false;
     781             :     }
     782             : 
     783           5 :     if (!mustWait) {
     784           0 :         task->activate(cx->runtime());
     785           5 :         HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     786             :     }
     787             : 
     788             :     return true;
     789             : }
     790             : 
     791             : bool
     792           0 : StartOffThreadParseTask(JSContext* cx, ParseTask* task, const ReadOnlyCompileOptions& options)
     793             : {
     794             :     // Suppress GC so that calls below do not trigger a new incremental GC
     795             :     // which could require barriers on the atoms zone.
     796          10 :     gc::AutoSuppressGC nogc(cx);
     797          10 :     gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(cx);
     798          10 :     AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
     799             : 
     800           0 :     JSObject* global = CreateGlobalForOffThreadParse(cx, nogc);
     801           5 :     if (!global)
     802             :         return false;
     803             : 
     804             :     // Mark the global's zone as created for a helper thread. This prevents it
     805             :     // from being collected until clearUsedByHelperThread() is called after
     806             :     // parsing is complete. If this function exits due to error this state is
     807             :     // cleared automatically.
     808           0 :     AutoSetCreatedForHelperThread createdForHelper(global);
     809             : 
     810           5 :     if (!task->init(cx, options, global))
     811             :         return false;
     812             : 
     813           0 :     if (!QueueOffThreadParseTask(cx, task))
     814             :         return false;
     815             : 
     816           5 :     createdForHelper.forget();
     817           0 :     return true;
     818             : }
     819             : 
     820             : bool
     821           5 : js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
     822             :                               const char16_t* chars, size_t length,
     823             :                               JS::OffThreadCompileCallback callback, void* callbackData)
     824             : {
     825          15 :     ScopedJSDeletePtr<ParseTask> task;
     826          10 :     task = cx->new_<ScriptParseTask>(cx, chars, length, callback, callbackData);
     827           0 :     if (!task || !StartOffThreadParseTask(cx, task, options))
     828             :         return false;
     829             : 
     830          10 :     task.forget();
     831           0 :     return true;
     832             : }
     833             : 
     834             : bool
     835           0 : js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
     836             :                               const char16_t* chars, size_t length,
     837             :                               JS::OffThreadCompileCallback callback, void* callbackData)
     838             : {
     839           0 :     ScopedJSDeletePtr<ParseTask> task;
     840           0 :     task = cx->new_<ModuleParseTask>(cx, chars, length, callback, callbackData);
     841           0 :     if (!task || !StartOffThreadParseTask(cx, task, options))
     842             :         return false;
     843             : 
     844           0 :     task.forget();
     845           0 :     return true;
     846             : }
     847             : 
     848             : bool
     849           0 : js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
     850             :                                const JS::TranscodeRange& range,
     851             :                                JS::OffThreadCompileCallback callback, void* callbackData)
     852             : {
     853           0 :     ScopedJSDeletePtr<ParseTask> task;
     854           0 :     task = cx->new_<ScriptDecodeTask>(cx, range, callback, callbackData);
     855           0 :     if (!task || !StartOffThreadParseTask(cx, task, options))
     856             :         return false;
     857             : 
     858           0 :     task.forget();
     859           0 :     return true;
     860             : }
     861             : 
     862             : bool
     863           0 : js::StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
     864             :                                      JS::TranscodeSources& sources,
     865             :                                      JS::OffThreadCompileCallback callback, void* callbackData)
     866             : {
     867           0 :     ScopedJSDeletePtr<ParseTask> task;
     868           0 :     task = cx->new_<MultiScriptsDecodeTask>(cx, sources, callback, callbackData);
     869           0 :     if (!task || !StartOffThreadParseTask(cx, task, options))
     870             :         return false;
     871             : 
     872           0 :     task.forget();
     873           0 :     return true;
     874             : }
     875             : 
     876             : #if defined(JS_BUILD_BINAST)
     877             : 
     878             : bool
     879           0 : js::StartOffThreadDecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
     880             :                                const uint8_t* buf, size_t length,
     881             :                                JS::OffThreadCompileCallback callback, void *callbackData)
     882             : {
     883           0 :     ScopedJSDeletePtr<ParseTask> task;
     884           0 :     task = cx->new_<BinASTDecodeTask>(cx, buf, length, callback, callbackData);
     885           0 :     if (!task || !StartOffThreadParseTask(cx, task, options))
     886             :         return false;
     887             : 
     888           0 :     task.forget();
     889           0 :     return true;
     890             : }
     891             : 
     892             : #endif /* JS_BUILD_BINAST */
     893             : 
     894             : void
     895           0 : js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
     896             : {
     897           0 :     MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
     898             : 
     899           0 :     GlobalHelperThreadState::ParseTaskVector newTasks;
     900             :     {
     901           0 :         AutoLockHelperThreadState lock;
     902             :         GlobalHelperThreadState::ParseTaskVector& waiting =
     903           0 :             HelperThreadState().parseWaitingOnGC(lock);
     904             : 
     905           0 :         for (size_t i = 0; i < waiting.length(); i++) {
     906           0 :             ParseTask* task = waiting[i];
     907           0 :             if (task->runtimeMatches(rt)) {
     908           0 :                 AutoEnterOOMUnsafeRegion oomUnsafe;
     909           0 :                 if (!newTasks.append(task))
     910           0 :                     oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
     911           0 :                 HelperThreadState().remove(waiting, &i);
     912             :             }
     913             :         }
     914             :     }
     915             : 
     916           0 :     if (newTasks.empty())
     917           0 :         return;
     918             : 
     919             :     // This logic should mirror the contents of the
     920             :     // !OffThreadParsingMustWaitForGC() branch in QueueOffThreadParseTask:
     921             : 
     922           0 :     for (size_t i = 0; i < newTasks.length(); i++)
     923           0 :         newTasks[i]->activate(rt);
     924             : 
     925           0 :     AutoLockHelperThreadState lock;
     926             : 
     927             :     {
     928           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     929           0 :         if (!HelperThreadState().parseWorklist(lock).appendAll(newTasks))
     930           0 :             oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
     931             :     }
     932             : 
     933           0 :     HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
     934             : }
     935             : 
     936             : #ifdef DEBUG
     937             : bool
     938        1447 : js::CurrentThreadIsParseThread()
     939             : {
     940        1447 :     JSContext* cx = TlsContext.get();
     941        4341 :     return cx->helperThread() && cx->helperThread()->parseTask();
     942             : }
     943             : #endif
     944             : 
     945             : static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
     946             : static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
     947             : 
     948             : // TSan enforces a minimum stack size that's just slightly larger than our
     949             : // default helper stack size.  It does this to store blobs of TSan-specific
     950             : // data on each thread's stack.  Unfortunately, that means that even though
     951             : // we'll actually receive a larger stack than we requested, the effective
     952             : // usable space of that stack is significantly less than what we expect.
     953             : // To offset TSan stealing our stack space from underneath us, double the
     954             : // default.
     955             : //
     956             : // Note that we don't need this for ASan/MOZ_ASAN because ASan doesn't
     957             : // require all the thread-specific state that TSan does.
     958             : #if defined(MOZ_TSAN)
     959             : static const uint32_t HELPER_STACK_SIZE = 2 * kDefaultHelperStackSize;
     960             : static const uint32_t HELPER_STACK_QUOTA = 2 * kDefaultHelperStackQuota;
     961             : #else
     962             : static const uint32_t HELPER_STACK_SIZE = kDefaultHelperStackSize;
     963             : static const uint32_t HELPER_STACK_QUOTA = kDefaultHelperStackQuota;
     964             : #endif
     965             : 
     966             : bool
     967           4 : GlobalHelperThreadState::ensureInitialized()
     968             : {
     969           0 :     MOZ_ASSERT(CanUseExtraThreads());
     970             : 
     971           4 :     MOZ_ASSERT(this == &HelperThreadState());
     972          12 :     AutoLockHelperThreadState lock;
     973             : 
     974           0 :     if (threads)
     975             :         return true;
     976             : 
     977           0 :     threads = js::MakeUnique<HelperThreadVector>();
     978           0 :     if (!threads || !threads->initCapacity(threadCount))
     979             :         return false;
     980             : 
     981           9 :     for (size_t i = 0; i < threadCount; i++) {
     982           4 :         threads->infallibleEmplaceBack();
     983           8 :         HelperThread& helper = (*threads)[i];
     984             : 
     985          16 :         helper.thread = mozilla::Some(Thread(Thread::Options().setStackSize(HELPER_STACK_SIZE)));
     986           1 :         if (!helper.thread->init(HelperThread::ThreadMain, &helper))
     987             :             goto error;
     988             : 
     989             :         continue;
     990             : 
     991             :     error:
     992             :         // Ensure that we do not leave uninitialized threads in the `threads`
     993             :         // vector.
     994           0 :         threads->popBack();
     995           0 :         finishThreads();
     996           0 :         return false;
     997             :     }
     998             : 
     999             :     return true;
    1000             : }
    1001             : 
    1002           0 : GlobalHelperThreadState::GlobalHelperThreadState()
    1003             :  : cpuCount(0),
    1004             :    threadCount(0),
    1005             :    threads(nullptr),
    1006             :    registerThread(nullptr),
    1007             :    unregisterThread(nullptr),
    1008             :    wasmTier2GeneratorsFinished_(0),
    1009          21 :    helperLock(mutexid::GlobalHelperThreadState)
    1010             : {
    1011           0 :     cpuCount = ClampDefaultCPUCount(GetCPUCount());
    1012           2 :     threadCount = ThreadCountForCPUCount(cpuCount);
    1013             : 
    1014           1 :     MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
    1015           1 : }
    1016             : 
    1017             : void
    1018           0 : GlobalHelperThreadState::finish()
    1019             : {
    1020           0 :     CancelOffThreadWasmTier2Generator();
    1021           0 :     finishThreads();
    1022             : 
    1023             :     // Make sure there are no Ion free tasks left. We check this here because,
    1024             :     // unlike the other tasks, we don't explicitly block on this when
    1025             :     // destroying a runtime.
    1026           0 :     AutoLockHelperThreadState lock;
    1027           0 :     auto& freeList = ionFreeList(lock);
    1028           0 :     while (!freeList.empty())
    1029           0 :         jit::FreeIonBuilder(freeList.popCopy());
    1030           0 : }
    1031             : 
    1032             : void
    1033           0 : GlobalHelperThreadState::finishThreads()
    1034             : {
    1035           0 :     if (!threads)
    1036             :         return;
    1037             : 
    1038           0 :     MOZ_ASSERT(CanUseExtraThreads());
    1039           0 :     for (auto& thread : *threads)
    1040           0 :         thread.destroy();
    1041           0 :     threads.reset(nullptr);
    1042             : }
    1043             : 
    1044             : void
    1045           0 : GlobalHelperThreadState::lock()
    1046             : {
    1047           0 :     helperLock.lock();
    1048           0 : }
    1049             : 
    1050             : void
    1051           0 : GlobalHelperThreadState::unlock()
    1052             : {
    1053           0 :     helperLock.unlock();
    1054           0 : }
    1055             : 
    1056             : #ifdef DEBUG
    1057             : bool
    1058           0 : GlobalHelperThreadState::isLockedByCurrentThread() const
    1059             : {
    1060           0 :     return helperLock.ownedByCurrentThread();
    1061             : }
    1062             : #endif // DEBUG
    1063             : 
    1064             : void
    1065           0 : GlobalHelperThreadState::wait(AutoLockHelperThreadState& locked, CondVar which,
    1066             :                               TimeDuration timeout /* = TimeDuration::Forever() */)
    1067             : {
    1068           1 :     whichWakeup(which).wait_for(locked, timeout);
    1069           0 : }
    1070             : 
    1071             : void
    1072           0 : GlobalHelperThreadState::notifyAll(CondVar which, const AutoLockHelperThreadState&)
    1073             : {
    1074           0 :     whichWakeup(which).notify_all();
    1075           0 : }
    1076             : 
    1077             : void
    1078           0 : GlobalHelperThreadState::notifyOne(CondVar which, const AutoLockHelperThreadState&)
    1079             : {
    1080           0 :     whichWakeup(which).notify_one();
    1081           0 : }
    1082             : 
    1083             : bool
    1084           0 : GlobalHelperThreadState::hasActiveThreads(const AutoLockHelperThreadState&)
    1085             : {
    1086           0 :     if (!threads)
    1087             :         return false;
    1088             : 
    1089           0 :     for (auto& thread : *threads) {
    1090           0 :         if (!thread.idle())
    1091             :             return true;
    1092             :     }
    1093             : 
    1094             :     return false;
    1095             : }
    1096             : 
    1097             : void
    1098           0 : GlobalHelperThreadState::waitForAllThreads()
    1099             : {
    1100           0 :     AutoLockHelperThreadState lock;
    1101           0 :     waitForAllThreadsLocked(lock);
    1102           0 : }
    1103             : 
    1104             : void
    1105           0 : GlobalHelperThreadState::waitForAllThreadsLocked(AutoLockHelperThreadState& lock)
    1106             : {
    1107           0 :     CancelOffThreadIonCompileLocked(CompilationSelector(AllCompilations()), false, lock);
    1108           0 :     CancelOffThreadWasmTier2GeneratorLocked(lock);
    1109             : 
    1110           0 :     while (hasActiveThreads(lock))
    1111           0 :         wait(lock, CONSUMER);
    1112           0 : }
    1113             : 
    1114             : // A task can be a "master" task, ie, it will block waiting for other worker
    1115             : // threads that perform work on its behalf.  If so it must not take the last
    1116             : // available thread; there must always be at least one worker thread able to do
    1117             : // the actual work.  (Or the system may deadlock.)
    1118             : //
    1119             : // If a task is a master task it *must* pass isMaster=true here, or perform a
    1120             : // similar calculation to avoid deadlock from starvation.
    1121             : //
    1122             : // isMaster should only be true if the thread calling checkTaskThreadLimit() is
    1123             : // a helper thread.
    1124             : //
    1125             : // NOTE: Calling checkTaskThreadLimit() from a helper thread in the dynamic
    1126             : // region after currentTask.emplace() and before currentTask.reset() may cause
    1127             : // it to return a different result than if it is called outside that dynamic
    1128             : // region, as the predicate inspects the values of the threads' currentTask
    1129             : // members.
    1130             : 
    1131             : template <typename T>
    1132             : bool
    1133           0 : GlobalHelperThreadState::checkTaskThreadLimit(size_t maxThreads, bool isMaster) const
    1134             : {
    1135           0 :     MOZ_ASSERT(maxThreads > 0);
    1136             : 
    1137           0 :     if (!isMaster && maxThreads >= threadCount)
    1138             :         return true;
    1139             : 
    1140          10 :     size_t count = 0;
    1141          10 :     size_t idle = 0;
    1142          70 :     for (auto& thread : *threads) {
    1143          40 :         if (thread.currentTask.isSome()) {
    1144           4 :             if (thread.currentTask->is<T>())
    1145           4 :                 count++;
    1146             :         } else {
    1147          36 :             idle++;
    1148             :         }
    1149          40 :         if (count >= maxThreads)
    1150             :             return false;
    1151             :     }
    1152             : 
    1153             :     // It is possible for the number of idle threads to be zero here, because
    1154             :     // checkTaskThreadLimit() can be called from non-helper threads.  Notably,
    1155             :     // the compression task scheduler invokes it, and runs off a helper thread.
    1156          10 :     if (idle == 0)
    1157             :         return false;
    1158             : 
    1159             :     // A master thread that's the last available thread must not be allowed to
    1160             :     // run.
    1161          10 :     if (isMaster && idle == 1)
    1162             :         return false;
    1163             : 
    1164          10 :     return true;
    1165             : }
    1166             : 
    1167             : struct MOZ_RAII AutoSetContextRuntime
    1168             : {
    1169             :     explicit AutoSetContextRuntime(JSRuntime* rt) {
    1170          76 :         TlsContext.get()->setRuntime(rt);
    1171             :     }
    1172             :     ~AutoSetContextRuntime() {
    1173          76 :         TlsContext.get()->setRuntime(nullptr);
    1174             :     }
    1175             : };
    1176             : 
    1177             : static inline bool
    1178             : IsHelperThreadSimulatingOOM(js::ThreadType threadType)
    1179             : {
    1180             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
    1181           0 :     return js::oom::targetThread == threadType;
    1182             : #else
    1183             :     return false;
    1184             : #endif
    1185             : }
    1186             : 
    1187             : void
    1188           0 : GlobalHelperThreadState::addSizeOfIncludingThis(JS::GlobalStats* stats,
    1189             :                                                 AutoLockHelperThreadState& lock) const
    1190             : {
    1191           0 :     MOZ_ASSERT(isLockedByCurrentThread());
    1192             : 
    1193           0 :     mozilla::MallocSizeOf mallocSizeOf = stats->mallocSizeOf_;
    1194           0 :     JS::HelperThreadStats& htStats = stats->helperThread;
    1195             : 
    1196           0 :     htStats.stateData += mallocSizeOf(this);
    1197             : 
    1198           0 :     if (threads)
    1199           0 :         htStats.stateData += threads->sizeOfIncludingThis(mallocSizeOf);
    1200             : 
    1201             :     // Report memory used by various containers
    1202           0 :     htStats.stateData +=
    1203           0 :         ionWorklist_.sizeOfExcludingThis(mallocSizeOf) +
    1204           0 :         ionFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
    1205           0 :         ionFreeList_.sizeOfExcludingThis(mallocSizeOf) +
    1206           0 :         wasmWorklist_tier1_.sizeOfExcludingThis(mallocSizeOf) +
    1207           0 :         wasmWorklist_tier2_.sizeOfExcludingThis(mallocSizeOf) +
    1208           0 :         wasmTier2GeneratorWorklist_.sizeOfExcludingThis(mallocSizeOf) +
    1209           0 :         promiseHelperTasks_.sizeOfExcludingThis(mallocSizeOf) +
    1210           0 :         parseWorklist_.sizeOfExcludingThis(mallocSizeOf) +
    1211           0 :         parseFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
    1212           0 :         parseWaitingOnGC_.sizeOfExcludingThis(mallocSizeOf) +
    1213           0 :         compressionPendingList_.sizeOfExcludingThis(mallocSizeOf) +
    1214           0 :         compressionWorklist_.sizeOfExcludingThis(mallocSizeOf) +
    1215           0 :         compressionFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
    1216           0 :         gcHelperWorklist_.sizeOfExcludingThis(mallocSizeOf) +
    1217           0 :         gcParallelWorklist_.sizeOfExcludingThis(mallocSizeOf);
    1218             : 
    1219             :     // Report ParseTasks on wait lists
    1220           0 :     for (auto task : parseWorklist_)
    1221           0 :         htStats.parseTask += task->sizeOfIncludingThis(mallocSizeOf);
    1222           0 :     for (auto task : parseFinishedList_)
    1223           0 :         htStats.parseTask += task->sizeOfIncludingThis(mallocSizeOf);
    1224           0 :     for (auto task : parseWaitingOnGC_)
    1225           0 :         htStats.parseTask += task->sizeOfIncludingThis(mallocSizeOf);
    1226             : 
    1227             :     // Report IonBuilders on wait lists
    1228           0 :     for (auto builder : ionWorklist_)
    1229           0 :         htStats.ionBuilder += builder->sizeOfExcludingThis(mallocSizeOf);
    1230           0 :     for (auto builder : ionFinishedList_)
    1231           0 :         htStats.ionBuilder += builder->sizeOfExcludingThis(mallocSizeOf);
    1232           0 :     for (auto builder : ionFreeList_)
    1233           0 :         htStats.ionBuilder += builder->sizeOfExcludingThis(mallocSizeOf);
    1234             : 
    1235             :     // Report wasm::CompileTasks on wait lists
    1236           0 :     for (auto task : wasmWorklist_tier1_)
    1237           0 :         htStats.wasmCompile += task->sizeOfIncludingThis(mallocSizeOf);
    1238           0 :     for (auto task : wasmWorklist_tier2_)
    1239           0 :         htStats.wasmCompile += task->sizeOfIncludingThis(mallocSizeOf);
    1240             : 
    1241             :     // Report number of helper threads.
    1242           0 :     MOZ_ASSERT(htStats.idleThreadCount == 0);
    1243           0 :     if (threads) {
    1244           0 :         for (auto& thread : *threads) {
    1245           0 :             if (thread.idle())
    1246           0 :                 htStats.idleThreadCount++;
    1247             :             else
    1248           0 :                 htStats.activeThreadCount++;
    1249             :         }
    1250             :     }
    1251           0 : }
    1252             : 
    1253             : size_t
    1254           0 : GlobalHelperThreadState::maxIonCompilationThreads() const
    1255             : {
    1256           0 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_ION))
    1257             :         return 1;
    1258         100 :     return threadCount;
    1259             : }
    1260             : 
    1261             : size_t
    1262           0 : GlobalHelperThreadState::maxWasmCompilationThreads() const
    1263             : {
    1264           0 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_WASM))
    1265             :         return 1;
    1266           0 :     return cpuCount;
    1267             : }
    1268             : 
    1269             : size_t
    1270           0 : GlobalHelperThreadState::maxWasmTier2GeneratorThreads() const
    1271             : {
    1272           0 :     return MaxTier2GeneratorTasks;
    1273             : }
    1274             : 
    1275             : size_t
    1276           0 : GlobalHelperThreadState::maxPromiseHelperThreads() const
    1277             : {
    1278           0 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_WASM))
    1279             :         return 1;
    1280           0 :     return cpuCount;
    1281             : }
    1282             : 
    1283             : size_t
    1284           0 : GlobalHelperThreadState::maxParseThreads() const
    1285             : {
    1286          10 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_PARSE))
    1287             :         return 1;
    1288          10 :     return cpuCount;
    1289             : }
    1290             : 
    1291             : size_t
    1292           0 : GlobalHelperThreadState::maxCompressionThreads() const
    1293             : {
    1294           0 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_COMPRESS))
    1295             :         return 1;
    1296             : 
    1297             :     // Compression is triggered on major GCs to compress ScriptSources. It is
    1298             :     // considered low priority work.
    1299             :     return 1;
    1300             : }
    1301             : 
    1302             : size_t
    1303           0 : GlobalHelperThreadState::maxGCHelperThreads() const
    1304             : {
    1305           0 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_GCHELPER))
    1306             :         return 1;
    1307           0 :     return threadCount;
    1308             : }
    1309             : 
    1310             : size_t
    1311           0 : GlobalHelperThreadState::maxGCParallelThreads() const
    1312             : {
    1313          42 :     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_GCPARALLEL))
    1314             :         return 1;
    1315           0 :     return threadCount;
    1316             : }
    1317             : 
    1318             : bool
    1319         183 : GlobalHelperThreadState::canStartWasmTier1Compile(const AutoLockHelperThreadState& lock)
    1320             : {
    1321           0 :     return canStartWasmCompile(lock, wasm::CompileMode::Tier1);
    1322             : }
    1323             : 
    1324             : bool
    1325         129 : GlobalHelperThreadState::canStartWasmTier2Compile(const AutoLockHelperThreadState& lock)
    1326             : {
    1327         129 :     return canStartWasmCompile(lock, wasm::CompileMode::Tier2);
    1328             : }
    1329             : 
    1330             : bool
    1331         312 : GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lock,
    1332             :                                              wasm::CompileMode mode)
    1333             : {
    1334         312 :     if (wasmWorklist(lock, mode).empty())
    1335             :         return false;
    1336             : 
    1337             :     // Parallel compilation and background compilation should be disabled on
    1338             :     // unicore systems.
    1339             : 
    1340           0 :     MOZ_RELEASE_ASSERT(cpuCount > 1);
    1341             : 
    1342             :     // If Tier2 is very backlogged we must give priority to it, since the Tier2
    1343             :     // queue holds onto Tier1 tasks.  Indeed if Tier2 is backlogged we will
    1344             :     // devote more resources to Tier2 and not start any Tier1 work at all.
    1345             : 
    1346           0 :     bool tier2oversubscribed = wasmTier2GeneratorWorklist(lock).length() > 20;
    1347             : 
    1348             :     // For Tier1 and Once compilation, honor the maximum allowed threads to
    1349             :     // compile wasm jobs at once, to avoid oversaturating the machine.
    1350             :     //
    1351             :     // For Tier2 compilation we need to allow other things to happen too, so we
    1352             :     // do not allow all logical cores to be used for background work; instead we
    1353             :     // wish to use a fraction of the physical cores.  We can't directly compute
    1354             :     // the physical cores from the logical cores, but 1/3 of the logical cores
    1355             :     // is a safe estimate for the number of physical cores available for
    1356             :     // background work.
    1357             : 
    1358           0 :     size_t physCoresAvailable = size_t(ceil(cpuCount / 3.0));
    1359             : 
    1360             :     size_t threads;
    1361           0 :     if (mode == wasm::CompileMode::Tier2) {
    1362           0 :         if (tier2oversubscribed)
    1363             :             threads = maxWasmCompilationThreads();
    1364             :         else
    1365             :             threads = physCoresAvailable;
    1366             :     } else {
    1367           0 :         if (tier2oversubscribed)
    1368             :             threads = 0;
    1369             :         else
    1370             :             threads = maxWasmCompilationThreads();
    1371             :     }
    1372             : 
    1373           0 :     if (!threads || !checkTaskThreadLimit<wasm::CompileTask*>(threads))
    1374             :         return false;
    1375             : 
    1376           0 :     return true;
    1377             : }
    1378             : 
    1379             : bool
    1380         129 : GlobalHelperThreadState::canStartWasmTier2Generator(const AutoLockHelperThreadState& lock)
    1381             : {
    1382           0 :     return !wasmTier2GeneratorWorklist(lock).empty() &&
    1383           0 :            checkTaskThreadLimit<wasm::Tier2GeneratorTask*>(maxWasmTier2GeneratorThreads(),
    1384           0 :                                                            /*isMaster=*/true);
    1385             : }
    1386             : 
    1387             : bool
    1388           0 : GlobalHelperThreadState::canStartPromiseHelperTask(const AutoLockHelperThreadState& lock)
    1389             : {
    1390             :     // PromiseHelperTasks can be wasm compilation tasks that in turn block on
    1391             :     // wasm compilation so set isMaster = true.
    1392         183 :     return !promiseHelperTasks(lock).empty() &&
    1393           0 :            checkTaskThreadLimit<PromiseHelperTask*>(maxPromiseHelperThreads(),
    1394         183 :                                                     /*isMaster=*/true);
    1395             : }
    1396             : 
    1397             : static bool
    1398           0 : IonBuilderHasHigherPriority(jit::IonBuilder* first, jit::IonBuilder* second)
    1399             : {
    1400             :     // Return true if priority(first) > priority(second).
    1401             :     //
    1402             :     // This method can return whatever it wants, though it really ought to be a
    1403             :     // total order. The ordering is allowed to race (change on the fly), however.
    1404             : 
    1405             :     // A lower optimization level indicates a higher priority.
    1406           0 :     if (first->optimizationInfo().level() != second->optimizationInfo().level())
    1407           0 :         return first->optimizationInfo().level() < second->optimizationInfo().level();
    1408             : 
    1409             :     // A script without an IonScript has precedence on one with.
    1410           0 :     if (first->scriptHasIonScript() != second->scriptHasIonScript())
    1411           0 :         return !first->scriptHasIonScript();
    1412             : 
    1413             :     // A higher warm-up counter indicates a higher priority.
    1414           0 :     return first->script()->getWarmUpCount() / first->script()->length() >
    1415           0 :            second->script()->getWarmUpCount() / second->script()->length();
    1416             : }
    1417             : 
    1418             : bool
    1419         283 : GlobalHelperThreadState::canStartIonCompile(const AutoLockHelperThreadState& lock)
    1420             : {
    1421         383 :     return !ionWorklist(lock).empty() &&
    1422           0 :            checkTaskThreadLimit<jit::IonBuilder*>(maxIonCompilationThreads());
    1423             : }
    1424             : 
    1425             : bool
    1426         178 : GlobalHelperThreadState::canStartIonFreeTask(const AutoLockHelperThreadState& lock)
    1427             : {
    1428         227 :     return !ionFreeList(lock).empty();
    1429             : }
    1430             : 
    1431             : jit::IonBuilder*
    1432          50 : GlobalHelperThreadState::highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock)
    1433             : {
    1434           0 :     auto& worklist = ionWorklist(lock);
    1435           0 :     MOZ_ASSERT(!worklist.empty());
    1436             : 
    1437             :     // Get the highest priority IonBuilder which has not started compilation yet.
    1438             :     size_t index = 0;
    1439          50 :     for (size_t i = 1; i < worklist.length(); i++) {
    1440           0 :         if (IonBuilderHasHigherPriority(worklist[i], worklist[index]))
    1441           0 :             index = i;
    1442             :     }
    1443             : 
    1444          50 :     jit::IonBuilder* builder = worklist[index];
    1445          50 :     worklist.erase(&worklist[index]);
    1446           0 :     return builder;
    1447             : }
    1448             : 
    1449             : bool
    1450         188 : GlobalHelperThreadState::canStartParseTask(const AutoLockHelperThreadState& lock)
    1451             : {
    1452             :     // Parse tasks that end up compiling asm.js in turn may use Wasm compilation
    1453             :     // threads to generate machine code.  We have no way (at present) to know
    1454             :     // ahead of time whether a parse task is going to parse asm.js content or
    1455             :     // not, so we just assume that all parse tasks are master tasks.
    1456         198 :     return !parseWorklist(lock).empty() &&
    1457         198 :            checkTaskThreadLimit<ParseTask*>(maxParseThreads(), /*isMaster=*/true);
    1458             : }
    1459             : 
    1460             : bool
    1461           0 : GlobalHelperThreadState::canStartCompressionTask(const AutoLockHelperThreadState& lock)
    1462             : {
    1463           0 :     return !compressionWorklist(lock).empty() &&
    1464         178 :            checkTaskThreadLimit<SourceCompressionTask*>(maxCompressionThreads());
    1465             : }
    1466             : 
    1467             : void
    1468           0 : GlobalHelperThreadState::startHandlingCompressionTasks(const AutoLockHelperThreadState& lock)
    1469             : {
    1470           0 :     scheduleCompressionTasks(lock);
    1471           0 :     if (canStartCompressionTask(lock))
    1472             :         notifyOne(PRODUCER, lock);
    1473           0 : }
    1474             : 
    1475             : void
    1476           0 : GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadState& lock)
    1477             : {
    1478           0 :     auto& pending = compressionPendingList(lock);
    1479           0 :     auto& worklist = compressionWorklist(lock);
    1480             : 
    1481           0 :     for (size_t i = 0; i < pending.length(); i++) {
    1482           0 :         if (pending[i]->shouldStart()) {
    1483             :             // OOMing during appending results in the task not being scheduled
    1484             :             // and deleted.
    1485           0 :             Unused << worklist.append(std::move(pending[i]));
    1486           0 :             remove(pending, &i);
    1487             :         }
    1488             :     }
    1489           0 : }
    1490             : 
    1491             : bool
    1492           0 : GlobalHelperThreadState::canStartGCHelperTask(const AutoLockHelperThreadState& lock)
    1493             : {
    1494         233 :     return !gcHelperWorklist(lock).empty() &&
    1495           0 :            checkTaskThreadLimit<GCHelperState*>(maxGCHelperThreads());
    1496             : }
    1497             : 
    1498             : bool
    1499         275 : GlobalHelperThreadState::canStartGCParallelTask(const AutoLockHelperThreadState& lock)
    1500             : {
    1501           0 :     return !gcParallelWorklist(lock).empty() &&
    1502           0 :            checkTaskThreadLimit<GCParallelTask*>(maxGCParallelThreads());
    1503             : }
    1504             : 
    1505           0 : js::GCParallelTask::~GCParallelTask()
    1506             : {
    1507             :     // Only most-derived classes' destructors may do the join: base class
    1508             :     // destructors run after those for derived classes' members, so a join in a
    1509             :     // base class can't ensure that the task is done using the members. All we
    1510             :     // can do now is check that someone has previously stopped the task.
    1511           0 :     assertNotStarted();
    1512           0 : }
    1513             : 
    1514             : bool
    1515           0 : js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
    1516             : {
    1517           0 :     assertNotStarted();
    1518             : 
    1519             :     // If we do the shutdown GC before running anything, we may never
    1520             :     // have initialized the helper threads. Just use the serial path
    1521             :     // since we cannot safely intialize them at this point.
    1522          42 :     if (!HelperThreadState().threads)
    1523             :         return false;
    1524             : 
    1525           0 :     if (!HelperThreadState().gcParallelWorklist(lock).append(this))
    1526             :         return false;
    1527           0 :     setDispatched(lock);
    1528             : 
    1529          42 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
    1530             : 
    1531          21 :     return true;
    1532             : }
    1533             : 
    1534             : bool
    1535           0 : js::GCParallelTask::start()
    1536             : {
    1537           0 :     AutoLockHelperThreadState helperLock;
    1538           0 :     return startWithLockHeld(helperLock);
    1539             : }
    1540             : 
    1541             : void
    1542          21 : js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock)
    1543             : {
    1544          42 :     if (isNotStarted(lock))
    1545             :         return;
    1546             : 
    1547           0 :     while (!isFinished(lock))
    1548           0 :         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
    1549             : 
    1550          19 :     setNotStarted(lock);
    1551          19 :     cancel_ = false;
    1552             : }
    1553             : 
    1554             : void
    1555           0 : js::GCParallelTask::join()
    1556             : {
    1557           0 :     AutoLockHelperThreadState helperLock;
    1558           0 :     joinWithLockHeld(helperLock);
    1559           0 : }
    1560             : 
    1561             : static inline
    1562             : TimeDuration
    1563          21 : TimeSince(TimeStamp prev)
    1564             : {
    1565          21 :     TimeStamp now = TimeStamp::Now();
    1566             :     // Sadly this happens sometimes.
    1567           0 :     MOZ_ASSERT(now >= prev);
    1568           0 :     if (now < prev)
    1569           0 :         now = prev;
    1570           0 :     return now - prev;
    1571             : }
    1572             : 
    1573             : void
    1574           0 : js::GCParallelTask::runFromMainThread(JSRuntime* rt)
    1575             : {
    1576           0 :     assertNotStarted();
    1577           0 :     MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
    1578           0 :     TimeStamp timeStart = TimeStamp::Now();
    1579           0 :     runTask();
    1580           0 :     duration_ = TimeSince(timeStart);
    1581           0 : }
    1582             : 
    1583             : void
    1584           0 : js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock)
    1585             : {
    1586           0 :     MOZ_ASSERT(isDispatched(lock));
    1587             : 
    1588          42 :     AutoSetContextRuntime ascr(runtime());
    1589          42 :     gc::AutoSetThreadIsPerformingGC performingGC;
    1590             : 
    1591             :     {
    1592           0 :         AutoUnlockHelperThreadState parallelSection(lock);
    1593          21 :         TimeStamp timeStart = TimeStamp::Now();
    1594          63 :         runTask();
    1595           0 :         duration_ = TimeSince(timeStart);
    1596          63 :     }
    1597           0 : 
    1598             :     setFinished(lock);
    1599             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, lock);
    1600          21 : }
    1601          42 : 
    1602           0 : bool
    1603             : js::GCParallelTask::isRunning() const
    1604             : {
    1605           0 :     AutoLockHelperThreadState lock;
    1606             :     return isRunningWithLockHeld(lock);
    1607           0 : }
    1608           0 : 
    1609             : void
    1610             : HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& lock)
    1611             : {
    1612           0 :     MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(lock));
    1613             :     MOZ_ASSERT(idle());
    1614          21 : 
    1615          21 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    1616             :     AutoTraceLog logCompile(logger, TraceLogger_GC);
    1617          21 : 
    1618          63 :     currentTask.emplace(HelperThreadState().gcParallelWorklist(lock).popCopy());
    1619             :     gcParallelTask()->runFromHelperThread(lock);
    1620           0 :     currentTask.reset();
    1621           0 : }
    1622          21 : 
    1623          21 : static void
    1624             : LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
    1625             : {
    1626           5 :     // Mark the zone as no longer in use by a helper thread, and available
    1627             :     // to be collected by the GC.
    1628             :     rt->clearUsedByHelperThread(task->parseGlobal->zoneFromAnyThread());
    1629             : }
    1630          10 : 
    1631           0 : ParseTask*
    1632             : GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, JS::OffThreadToken* token)
    1633             : {
    1634           0 :     // The token is really a ParseTask* which should be in the finished list.
    1635             :     // Remove its entry.
    1636             :     auto task = static_cast<ParseTask*>(token);
    1637             :     MOZ_ASSERT(task->kind == kind);
    1638           5 : 
    1639           5 :     AutoLockHelperThreadState lock;
    1640             : 
    1641          15 : #ifdef DEBUG
    1642             :     auto& finished = parseFinishedList(lock);
    1643             :     bool found = false;
    1644          10 :     for (auto t : finished) {
    1645           0 :         if (t == task) {
    1646           0 :             found = true;
    1647           5 :             break;
    1648             :         }
    1649             :     }
    1650             :     MOZ_ASSERT(found);
    1651             : #endif
    1652           5 : 
    1653             :     task->remove();
    1654             :     return task;
    1655           5 : }
    1656           0 : 
    1657             : template <typename F, typename>
    1658             : bool
    1659             : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
    1660             :                                          JS::OffThreadToken* token, F&& finishCallback)
    1661           0 : {
    1662             :     MOZ_ASSERT(cx->realm());
    1663             : 
    1664           5 :     ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
    1665             : 
    1666          15 :     // Make sure we have all the constructors we need for the prototype
    1667             :     // remapping below, since we can't GC while that's happening.
    1668             :     if (!EnsureParserCreatedClasses(cx, kind)) {
    1669             :         LeaveParseTaskZone(cx->runtime(), parseTask);
    1670           0 :         return false;
    1671           0 :     }
    1672           0 : 
    1673             :     mergeParseTaskRealm(cx, parseTask, cx->realm());
    1674             : 
    1675           5 :     bool ok = finishCallback(parseTask);
    1676             : 
    1677           0 :     for (auto& script : parseTask->scripts)
    1678             :         releaseAssertSameCompartment(cx, script);
    1679          15 : 
    1680           5 :     if (!parseTask->finish(cx) || !ok)
    1681             :         return false;
    1682           5 : 
    1683             :     // Report out of memory errors eagerly, or errors could be malformed.
    1684             :     if (parseTask->outOfMemory) {
    1685             :         ReportOutOfMemory(cx);
    1686           1 :         return false;
    1687           0 :     }
    1688           0 : 
    1689             :     // Report any error or warnings generated during the parse, and inform the
    1690             :     // debugger about the compiled scripts.
    1691             :     for (size_t i = 0; i < parseTask->errors.length(); i++)
    1692             :         parseTask->errors[i]->throwError(cx);
    1693           5 :     if (parseTask->overRecursed)
    1694           0 :         ReportOverRecursed(cx);
    1695           5 :     if (cx->isExceptionPending())
    1696           0 :         return false;
    1697           0 : 
    1698             :     return true;
    1699             : }
    1700           0 : 
    1701             : JSScript*
    1702             : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
    1703             :                                          JS::OffThreadToken* token)
    1704           5 : {
    1705             :     JS::RootedScript script(cx);
    1706             : 
    1707          10 :     bool ok = finishParseTask(cx, kind, token, [&script] (ParseTask* parseTask) {
    1708             :         MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
    1709          20 : 
    1710          10 :         if (parseTask->scripts.length() > 0)
    1711             :             script = parseTask->scripts[0];
    1712          10 : 
    1713          15 :         return true;
    1714             :     });
    1715           5 : 
    1716           5 :     if (!ok)
    1717             :         return nullptr;
    1718           5 : 
    1719             :     if (!script) {
    1720             :         // No error was reported, but no script produced. Assume we hit out of
    1721           5 :         // memory.
    1722             :         MOZ_ASSERT(false, "Expected script");
    1723             :         ReportOutOfMemory(cx);
    1724           0 :         return nullptr;
    1725             :     }
    1726             : 
    1727             :     // The Debugger only needs to be told about the topmost script that was compiled.
    1728             :     Debugger::onNewScript(cx, script);
    1729             : 
    1730           0 :     return script;
    1731             : }
    1732           0 : 
    1733             : bool
    1734             : GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
    1735             :                                          JS::OffThreadToken* token,
    1736           0 :                                          MutableHandle<ScriptVector> scripts)
    1737             : {
    1738             :     size_t expectedLength = 0;
    1739             : 
    1740           0 :     bool ok = finishParseTask(cx, kind, token, [&scripts, &expectedLength] (ParseTask* parseTask) {
    1741             :         MOZ_ASSERT(parseTask->kind == ParseTaskKind::MultiScriptsDecode);
    1742           0 :         auto task = static_cast<MultiScriptsDecodeTask*>(parseTask);
    1743           0 : 
    1744           0 :         expectedLength = task->sources->length();
    1745             : 
    1746           0 :         if (!scripts.reserve(task->scripts.length()))
    1747             :             return false;
    1748           0 : 
    1749             :         for (auto& script : task->scripts)
    1750             :             scripts.infallibleAppend(script);
    1751           0 :         return true;
    1752           0 :     });
    1753             : 
    1754           0 :     if (!ok)
    1755             :         return false;
    1756           0 : 
    1757             :     if (scripts.length() != expectedLength) {
    1758             :         // No error was reported, but fewer scripts produced than expected.
    1759           0 :         // Assume we hit out of memory.
    1760             :         MOZ_ASSERT(false, "Expected more scripts");
    1761             :         ReportOutOfMemory(cx);
    1762           0 :         return false;
    1763             :     }
    1764             : 
    1765             :     // The Debugger only needs to be told about the topmost script that was compiled.
    1766             :     JS::RootedScript rooted(cx);
    1767             :     for (auto& script : scripts) {
    1768           0 :         MOZ_ASSERT(script->isGlobalCode());
    1769           0 : 
    1770           0 :         rooted = script;
    1771             :         Debugger::onNewScript(cx, rooted);
    1772           0 :     }
    1773           0 : 
    1774             :     return true;
    1775             : }
    1776             : 
    1777             : JSScript*
    1778             : GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, JS::OffThreadToken* token)
    1779             : {
    1780           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::Script, token);
    1781             :     MOZ_ASSERT_IF(script, script->isGlobalCode());
    1782           0 :     return script;
    1783          10 : }
    1784           5 : 
    1785             : JSScript*
    1786             : GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, JS::OffThreadToken* token)
    1787             : {
    1788           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
    1789             :     MOZ_ASSERT_IF(script, script->isGlobalCode());
    1790           0 :     return script;
    1791           0 : }
    1792           0 : 
    1793             : #if defined(JS_BUILD_BINAST)
    1794             : 
    1795             : JSScript*
    1796             : GlobalHelperThreadState::finishBinASTDecodeTask(JSContext* cx, JS::OffThreadToken* token)
    1797             : {
    1798           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::BinAST, token);
    1799             :     MOZ_ASSERT_IF(script, script->isGlobalCode());
    1800           0 :     return script;
    1801           0 : }
    1802           0 : 
    1803             : #endif /* JS_BUILD_BINAST */
    1804             : 
    1805             : bool
    1806             : GlobalHelperThreadState::finishMultiScriptsDecodeTask(JSContext* cx, JS::OffThreadToken* token,
    1807             :                                                       MutableHandle<ScriptVector> scripts)
    1808           0 : {
    1809             :     return finishParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
    1810             : }
    1811           0 : 
    1812             : JSObject*
    1813             : GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, JS::OffThreadToken* token)
    1814             : {
    1815           0 :     JSScript* script = finishParseTask(cx, ParseTaskKind::Module, token);
    1816             :     if (!script)
    1817           0 :         return nullptr;
    1818           0 : 
    1819             :     MOZ_ASSERT(script->module());
    1820             : 
    1821           0 :     RootedModuleObject module(cx, script->module());
    1822             :     module->fixEnvironmentsAfterCompartmentMerge();
    1823           0 :     if (!ModuleObject::Freeze(cx, module))
    1824           0 :         return nullptr;
    1825           0 : 
    1826             :     return module;
    1827             : }
    1828           0 : 
    1829             : void
    1830             : GlobalHelperThreadState::cancelParseTask(JSRuntime* rt, ParseTaskKind kind,
    1831             :                                          JS::OffThreadToken* token)
    1832           0 : {
    1833             :     destroyParseTask(rt, removeFinishedParseTask(kind, token));
    1834             : }
    1835           0 : 
    1836           0 : void
    1837             : GlobalHelperThreadState::destroyParseTask(JSRuntime* rt, ParseTask* parseTask)
    1838             : {
    1839           0 :     MOZ_ASSERT(!parseTask->isInList());
    1840             :     LeaveParseTaskZone(rt, parseTask);
    1841           0 :     js_delete(parseTask);
    1842           0 : }
    1843           0 : 
    1844           0 : void
    1845             : GlobalHelperThreadState::mergeParseTaskRealm(JSContext* cx, ParseTask* parseTask, Realm* dest)
    1846             : {
    1847           0 :     // After we call LeaveParseTaskZone() it's not safe to GC until we have
    1848             :     // finished merging the contents of the parse task's realm into the
    1849             :     // destination realm.
    1850             :     JS::AutoAssertNoGC nogc(cx);
    1851             : 
    1852          10 :     LeaveParseTaskZone(cx->runtime(), parseTask);
    1853             : 
    1854           5 :     // Move the parsed script and all its contents into the desired realm.
    1855             :     gc::MergeRealms(parseTask->parseGlobal->realm(), dest);
    1856             : }
    1857          10 : 
    1858           5 : void
    1859             : HelperThread::destroy()
    1860             : {
    1861           0 :     if (thread.isSome()) {
    1862             :         {
    1863           0 :             AutoLockHelperThreadState lock;
    1864             :             terminate = true;
    1865           0 : 
    1866           0 :             /* Notify all helpers, to ensure that this thread wakes up. */
    1867             :             HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
    1868             :         }
    1869           0 : 
    1870             :         thread->join();
    1871             :         thread.reset();
    1872           0 :     }
    1873           0 : }
    1874             : 
    1875           0 : void
    1876             : HelperThread::ensureRegisteredWithProfiler()
    1877             : {
    1878           0 :     if (registered)
    1879             :         return;
    1880           0 : 
    1881             :     JS::RegisterThreadCallback callback = HelperThreadState().registerThread;
    1882             :     if (callback) {
    1883           8 :         callback("JS Helper", reinterpret_cast<void*>(GetNativeStackBase()));
    1884           0 :         registered = true;
    1885           4 :     }
    1886           0 : }
    1887             : 
    1888             : void
    1889             : HelperThread::unregisterWithProfilerIfNeeded()
    1890             : {
    1891           0 :     if (!registered)
    1892             :         return;
    1893           0 : 
    1894             :     JS::UnregisterThreadCallback callback = HelperThreadState().unregisterThread;
    1895             :     if (callback) {
    1896           0 :         callback();
    1897           0 :         registered = false;
    1898           0 :     }
    1899           0 : }
    1900             : 
    1901             : /* static */
    1902             : void
    1903             : HelperThread::ThreadMain(void* arg)
    1904             : {
    1905           4 :     ThisThread::SetName("JS Helper");
    1906             : 
    1907           0 :     static_cast<HelperThread*>(arg)->threadLoop();
    1908             :     Mutex::ShutDown();
    1909           4 : }
    1910           0 : 
    1911           0 : void
    1912             : HelperThread::handleWasmTier1Workload(AutoLockHelperThreadState& locked)
    1913             : {
    1914           0 :     handleWasmWorkload(locked, wasm::CompileMode::Tier1);
    1915             : }
    1916           0 : 
    1917           0 : void
    1918             : HelperThread::handleWasmTier2Workload(AutoLockHelperThreadState& locked)
    1919             : {
    1920           0 :     handleWasmWorkload(locked, wasm::CompileMode::Tier2);
    1921             : }
    1922           0 : 
    1923           0 : void
    1924             : HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode)
    1925             : {
    1926           0 :     MOZ_ASSERT(HelperThreadState().canStartWasmCompile(locked, mode));
    1927             :     MOZ_ASSERT(idle());
    1928           0 : 
    1929           0 :     currentTask.emplace(HelperThreadState().wasmWorklist(locked, mode).popCopyFront());
    1930             : 
    1931           0 :     wasm::CompileTask* task = wasmTask();
    1932             :     {
    1933           0 :         AutoUnlockHelperThreadState unlock(locked);
    1934             :         wasm::ExecuteCompileTaskFromHelperThread(task);
    1935           0 :     }
    1936           0 : 
    1937             :     currentTask.reset();
    1938             : 
    1939           0 :     // Since currentTask is only now reset(), this could be the last active thread
    1940             :     // waitForAllThreads() is waiting for. No one else should be waiting, though.
    1941             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1942             : }
    1943           0 : 
    1944           0 : void
    1945             : HelperThread::handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked)
    1946             : {
    1947           0 :     MOZ_ASSERT(HelperThreadState().canStartWasmTier2Generator(locked));
    1948             :     MOZ_ASSERT(idle());
    1949           0 : 
    1950           0 :     currentTask.emplace(HelperThreadState().wasmTier2GeneratorWorklist(locked).popCopy());
    1951             : 
    1952           0 :     wasm::Tier2GeneratorTask* task = wasmTier2GeneratorTask();
    1953             :     {
    1954           0 :         AutoUnlockHelperThreadState unlock(locked);
    1955             :         task->execute();
    1956           0 :     }
    1957           0 : 
    1958             :     currentTask.reset();
    1959             :     js_delete(task);
    1960           0 : 
    1961           0 :     // During shutdown the main thread will wait for any ongoing (cancelled)
    1962             :     // tier-2 generation to shut down normally.  To do so, it waits on the
    1963             :     // CONSUMER condition for the count of finished generators to rise.
    1964             :     HelperThreadState().incWasmTier2GeneratorsFinished(locked);
    1965             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1966           0 : }
    1967           0 : 
    1968           0 : void
    1969             : HelperThread::handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked)
    1970             : {
    1971           0 :     MOZ_ASSERT(HelperThreadState().canStartPromiseHelperTask(locked));
    1972             :     MOZ_ASSERT(idle());
    1973           0 : 
    1974           0 :     PromiseHelperTask* task = HelperThreadState().promiseHelperTasks(locked).popCopy();
    1975             :     currentTask.emplace(task);
    1976           0 : 
    1977           0 :     {
    1978             :         AutoUnlockHelperThreadState unlock(locked);
    1979             :         task->execute();
    1980           0 :         task->dispatchResolveAndDestroy();
    1981           0 :     }
    1982           0 : 
    1983             :     currentTask.reset();
    1984             : 
    1985           0 :     // Since currentTask is only now reset(), this could be the last active thread
    1986             :     // waitForAllThreads() is waiting for. No one else should be waiting, though.
    1987             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    1988             : }
    1989           0 : 
    1990           0 : void
    1991             : HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
    1992             : {
    1993          50 :     MOZ_ASSERT(HelperThreadState().canStartIonCompile(locked));
    1994             :     MOZ_ASSERT(idle());
    1995          50 : 
    1996           0 :     // Find the IonBuilder in the worklist with the highest priority, and
    1997             :     // remove it from the worklist.
    1998             :     jit::IonBuilder* builder = HelperThreadState().highestPriorityPendingIonCompile(locked);
    1999             : 
    2000           0 :     currentTask.emplace(builder);
    2001             : 
    2002          50 :     JSRuntime* rt = builder->script()->compartment()->runtimeFromAnyThread();
    2003             : 
    2004         100 :     {
    2005             :         AutoUnlockHelperThreadState unlock(locked);
    2006             : 
    2007         150 :         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    2008             :         TraceLoggerEvent event(TraceLogger_AnnotateScripts, builder->script());
    2009          50 :         AutoTraceLog logScript(logger, event);
    2010           0 :         AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
    2011         150 : 
    2012           0 :         AutoSetContextRuntime ascr(rt);
    2013             :         jit::JitContext jctx(jit::CompileRuntime::get(rt),
    2014          50 :                              jit::CompileRealm::get(builder->script()->realm()),
    2015             :                              &builder->alloc());
    2016             :         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
    2017         100 :     }
    2018           0 : 
    2019             :     FinishOffThreadIonCompile(builder, locked);
    2020             : 
    2021          50 :     // Ping the main thread so that the compiled code can be incorporated at the
    2022             :     // next interrupt callback.
    2023             :     //
    2024             :     // This must happen before the current task is reset. DestroyContext
    2025             :     // cancels in progress Ion compilations before destroying its target
    2026             :     // context, and after we reset the current task we are no longer considered
    2027             :     // to be Ion compiling.
    2028             :     rt->mainContextFromAnyThread()->requestInterrupt(InterruptReason::AttachIonCompilations);
    2029             : 
    2030          50 :     currentTask.reset();
    2031             : 
    2032          50 :     // Notify the main thread in case it is waiting for the compilation to finish.
    2033             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    2034             : }
    2035         100 : 
    2036           0 : void
    2037             : HelperThread::handleIonFreeWorkload(AutoLockHelperThreadState& locked)
    2038             : {
    2039          49 :     MOZ_ASSERT(idle());
    2040             :     MOZ_ASSERT(HelperThreadState().canStartIonFreeTask(locked));
    2041           0 : 
    2042          98 :     auto& freeList = HelperThreadState().ionFreeList(locked);
    2043             : 
    2044          98 :     jit::IonBuilder* builder = freeList.popCopy();
    2045             :     {
    2046          49 :         AutoUnlockHelperThreadState unlock(locked);
    2047             :         FreeIonBuilder(builder);
    2048           0 :     }
    2049           0 : }
    2050             : 
    2051          49 : HelperThread*
    2052             : js::CurrentHelperThread()
    2053             : {
    2054           8 :     if (!HelperThreadState().threads)
    2055             :         return nullptr;
    2056           0 :     auto threadId = ThisThread::GetId();
    2057             :     for (auto& thisThread : *HelperThreadState().threads) {
    2058           8 :         if (thisThread.thread.isSome() && threadId == thisThread.thread->get_id())
    2059          46 :             return &thisThread;
    2060           0 :     }
    2061             :     return nullptr;
    2062             : }
    2063             : 
    2064             : bool
    2065             : JSContext::addPendingCompileError(js::CompileError** error)
    2066             : {
    2067           0 :     auto errorPtr = make_unique<js::CompileError>();
    2068             :     if (!errorPtr)
    2069           0 :         return false;
    2070           0 :     if (!helperThread()->parseTask()->errors.append(errorPtr.get())) {
    2071             :         ReportOutOfMemory(this);
    2072           0 :         return false;
    2073           0 :     }
    2074           0 :     *error = errorPtr.release();
    2075             :     return true;
    2076           0 : }
    2077           0 : 
    2078             : void
    2079             : JSContext::addPendingOverRecursed()
    2080             : {
    2081           0 :     if (helperThread()->parseTask())
    2082             :         helperThread()->parseTask()->overRecursed = true;
    2083           0 : }
    2084           0 : 
    2085           0 : void
    2086             : JSContext::addPendingOutOfMemory()
    2087             : {
    2088           0 :     // Keep in sync with recoverFromOutOfMemory.
    2089             :     if (helperThread()->parseTask())
    2090             :         helperThread()->parseTask()->outOfMemory = true;
    2091           0 : }
    2092           0 : 
    2093           0 : void
    2094             : HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked)
    2095             : {
    2096           5 :     MOZ_ASSERT(HelperThreadState().canStartParseTask(locked));
    2097             :     MOZ_ASSERT(idle());
    2098           0 : 
    2099           5 :     currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
    2100             :     ParseTask* task = parseTask();
    2101           0 : 
    2102           5 :     {
    2103             :         AutoUnlockHelperThreadState unlock(locked);
    2104             :         AutoSetContextRuntime ascr(task->parseGlobal->runtimeFromAnyThread());
    2105          15 : 
    2106           0 :         JSContext* cx = TlsContext.get();
    2107             : 
    2108           0 :         Zone* zone = task->parseGlobal->zoneFromAnyThread();
    2109             :         zone->setHelperThreadOwnerContext(cx);
    2110           0 :         auto resetOwnerContext = mozilla::MakeScopeExit([&] {
    2111           0 :             zone->setHelperThreadOwnerContext(nullptr);
    2112             :         });
    2113           5 : 
    2114          15 :         AutoRealm ar(cx, task->parseGlobal);
    2115             : 
    2116          15 :         task->parse(cx);
    2117             : 
    2118           0 :         cx->frontendCollectionPool().purge();
    2119             :     }
    2120           0 : 
    2121             :     // The callback is invoked while we are still off thread.
    2122             :     task->callback(task, task->callbackData);
    2123             : 
    2124           0 :     // FinishOffThreadScript will need to be called on the script to
    2125             :     // migrate it into the correct compartment.
    2126             :     HelperThreadState().parseFinishedList(locked).insertBack(task);
    2127             : 
    2128          10 :     currentTask.reset();
    2129             : 
    2130           5 :     // Notify the main thread in case it is waiting for the parse/emit to finish.
    2131             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    2132             : }
    2133           0 : 
    2134           5 : void
    2135             : HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
    2136             : {
    2137           0 :     MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
    2138             :     MOZ_ASSERT(idle());
    2139           0 : 
    2140           0 :     UniquePtr<SourceCompressionTask> task;
    2141             :     {
    2142           0 :         auto& worklist = HelperThreadState().compressionWorklist(locked);
    2143             :         task = std::move(worklist.back());
    2144           0 :         worklist.popBack();
    2145           0 :         currentTask.emplace(task.get());
    2146           0 :     }
    2147           0 : 
    2148             :     {
    2149             :         AutoUnlockHelperThreadState unlock(locked);
    2150             : 
    2151           0 :         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
    2152             :         AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
    2153           0 : 
    2154           0 :         task->work();
    2155             :     }
    2156           0 : 
    2157             :     {
    2158             :         AutoEnterOOMUnsafeRegion oomUnsafe;
    2159             :         if (!HelperThreadState().compressionFinishedList(locked).append(std::move(task)))
    2160           0 :             oomUnsafe.crash("handleCompressionWorkload");
    2161           0 :     }
    2162           0 : 
    2163             :     currentTask.reset();
    2164             : 
    2165           0 :     // Notify the main thread in case it is waiting for the compression to finish.
    2166             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    2167             : }
    2168           0 : 
    2169           0 : bool
    2170             : js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
    2171             : {
    2172         414 :     AutoLockHelperThreadState lock;
    2173             : 
    2174        1242 :     auto& pending = HelperThreadState().compressionPendingList(lock);
    2175             :     if (!pending.append(std::move(task))) {
    2176           0 :         if (!cx->helperThread())
    2177           0 :             ReportOutOfMemory(cx);
    2178           0 :         return false;
    2179           0 :     }
    2180             : 
    2181             :     return true;
    2182             : }
    2183             : 
    2184             : template <typename T>
    2185             : static void
    2186             : ClearCompressionTaskList(T& list, JSRuntime* runtime)
    2187             : {
    2188           0 :     for (size_t i = 0; i < list.length(); i++) {
    2189             :         if (list[i]->runtimeMatches(runtime))
    2190           0 :             HelperThreadState().remove(list, &i);
    2191           0 :     }
    2192           0 : }
    2193             : 
    2194           0 : void
    2195             : js::CancelOffThreadCompressions(JSRuntime* runtime)
    2196             : {
    2197           0 :     AutoLockHelperThreadState lock;
    2198             : 
    2199           0 :     if (!HelperThreadState().threads)
    2200             :         return;
    2201           0 : 
    2202           0 :     // Cancel all pending compression tasks.
    2203             :     ClearCompressionTaskList(HelperThreadState().compressionPendingList(lock), runtime);
    2204             :     ClearCompressionTaskList(HelperThreadState().compressionWorklist(lock), runtime);
    2205           0 : 
    2206           0 :     // Cancel all in-process compression tasks and wait for them to join so we
    2207             :     // clean up the finished tasks.
    2208             :     while (true) {
    2209             :         bool inProgress = false;
    2210             :         for (auto& thread : *HelperThreadState().threads) {
    2211           0 :             SourceCompressionTask* task = thread.compressionTask();
    2212           0 :             if (task && task->runtimeMatches(runtime))
    2213           0 :                 inProgress = true;
    2214           0 :         }
    2215           0 : 
    2216             :         if (!inProgress)
    2217             :             break;
    2218           0 : 
    2219             :         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
    2220             :     }
    2221           0 : 
    2222             :     // Clean up finished tasks.
    2223             :     ClearCompressionTaskList(HelperThreadState().compressionFinishedList(lock), runtime);
    2224             : }
    2225           0 : 
    2226             : void
    2227             : PromiseHelperTask::executeAndResolveAndDestroy(JSContext* cx)
    2228             : {
    2229           0 :     execute();
    2230             :     run(cx, JS::Dispatchable::NotShuttingDown);
    2231           0 : }
    2232           0 : 
    2233           0 : bool
    2234             : js::StartOffThreadPromiseHelperTask(JSContext* cx, UniquePtr<PromiseHelperTask> task)
    2235             : {
    2236           0 :     // Execute synchronously if there are no helper threads.
    2237             :     if (!CanUseExtraThreads()) {
    2238             :         task.release()->executeAndResolveAndDestroy(cx);
    2239           0 :         return true;
    2240           0 :     }
    2241           0 : 
    2242             :     AutoLockHelperThreadState lock;
    2243             : 
    2244           0 :     if (!HelperThreadState().promiseHelperTasks(lock).append(task.get())) {
    2245             :         ReportOutOfMemory(cx);
    2246           0 :         return false;
    2247           0 :     }
    2248           0 : 
    2249             :     Unused << task.release();
    2250             : 
    2251           0 :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
    2252             :     return true;
    2253           0 : }
    2254           0 : 
    2255             : bool
    2256             : js::StartOffThreadPromiseHelperTask(PromiseHelperTask* task)
    2257             : {
    2258           0 :     MOZ_ASSERT(CanUseExtraThreads());
    2259             : 
    2260           0 :     AutoLockHelperThreadState lock;
    2261             : 
    2262           0 :     if (!HelperThreadState().promiseHelperTasks(lock).append(task))
    2263             :         return false;
    2264           0 : 
    2265             :     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
    2266             :     return true;
    2267           0 : }
    2268           0 : 
    2269             : void
    2270             : GlobalHelperThreadState::trace(JSTracer* trc, gc::AutoTraceSession& session)
    2271             : {
    2272           0 :     AutoLockHelperThreadState lock;
    2273             :     for (auto builder : ionWorklist(lock))
    2274           0 :         builder->trace(trc);
    2275           0 :     for (auto builder : ionFinishedList(lock))
    2276           0 :         builder->trace(trc);
    2277           0 : 
    2278           0 :     if (HelperThreadState().threads) {
    2279             :         for (auto& helper : *HelperThreadState().threads) {
    2280           0 :             if (auto builder = helper.ionBuilder())
    2281           0 :                 builder->trace(trc);
    2282          16 :         }
    2283           0 :     }
    2284             : 
    2285             :     JSRuntime* rt = trc->runtime();
    2286             :     if (auto* jitRuntime = rt->jitRuntime()) {
    2287           4 :         jit::IonBuilder* builder = jitRuntime->ionLazyLinkList(rt).getFirst();
    2288           4 :         while (builder) {
    2289           0 :             builder->trace(trc);
    2290           4 :             builder = builder->getNext();
    2291           0 :         }
    2292           0 :     }
    2293             : 
    2294             :     for (auto parseTask : parseWorklist_)
    2295             :         parseTask->trace(trc);
    2296           0 :     for (auto parseTask : parseFinishedList_)
    2297           0 :         parseTask->trace(trc);
    2298           0 :     for (auto parseTask : parseWaitingOnGC_)
    2299           0 :         parseTask->trace(trc);
    2300           8 : }
    2301           0 : 
    2302           4 : void
    2303             : HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
    2304             : {
    2305           0 :     MOZ_ASSERT(HelperThreadState().canStartGCHelperTask(locked));
    2306             :     MOZ_ASSERT(idle());
    2307           0 : 
    2308           0 :     currentTask.emplace(HelperThreadState().gcHelperWorklist(locked).popCopy());
    2309             :     GCHelperState* task = gcHelperTask();
    2310           0 : 
    2311           0 :     AutoSetContextRuntime ascr(task->runtime());
    2312             : 
    2313           0 :     {
    2314             :         AutoUnlockHelperThreadState unlock(locked);
    2315             :         task->work();
    2316           0 :     }
    2317           0 : 
    2318             :     currentTask.reset();
    2319             :     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
    2320           0 : }
    2321           0 : 
    2322           0 : void
    2323             : JSContext::setHelperThread(HelperThread* thread)
    2324             : {
    2325           4 :     if (helperThread_)
    2326             :         nurserySuppressions_--;
    2327           8 : 
    2328           0 :     helperThread_ = thread;
    2329             : 
    2330           8 :     if (helperThread_)
    2331             :         nurserySuppressions_++;
    2332           8 : }
    2333           4 : 
    2334           4 : // Definition of helper thread tasks.
    2335             : //
    2336             : // Priority is determined by the order they're listed here.
    2337             : const HelperThread::TaskSpec HelperThread::taskSpecs[] = {
    2338             :     {
    2339             :         THREAD_TYPE_GCPARALLEL,
    2340             :         &GlobalHelperThreadState::canStartGCParallelTask,
    2341             :         &HelperThread::handleGCParallelWorkload
    2342             :     },
    2343             :     {
    2344             :         THREAD_TYPE_GCHELPER,
    2345             :         &GlobalHelperThreadState::canStartGCHelperTask,
    2346             :         &HelperThread::handleGCHelperWorkload
    2347             :     },
    2348             :     {
    2349             :         THREAD_TYPE_ION,
    2350             :         &GlobalHelperThreadState::canStartIonCompile,
    2351             :         &HelperThread::handleIonWorkload
    2352             :     },
    2353             :     {
    2354             :         THREAD_TYPE_WASM,
    2355             :         &GlobalHelperThreadState::canStartWasmTier1Compile,
    2356             :         &HelperThread::handleWasmTier1Workload
    2357             :     },
    2358             :     {
    2359             :         THREAD_TYPE_PROMISE_TASK,
    2360             :         &GlobalHelperThreadState::canStartPromiseHelperTask,
    2361             :         &HelperThread::handlePromiseHelperTaskWorkload
    2362             :     },
    2363             :     {
    2364             :         THREAD_TYPE_PARSE,
    2365             :         &GlobalHelperThreadState::canStartParseTask,
    2366             :         &HelperThread::handleParseWorkload
    2367             :     },
    2368             :     {
    2369             :         THREAD_TYPE_COMPRESS,
    2370             :         &GlobalHelperThreadState::canStartCompressionTask,
    2371             :         &HelperThread::handleCompressionWorkload
    2372             :     },
    2373             :     {
    2374             :         THREAD_TYPE_ION_FREE,
    2375             :         &GlobalHelperThreadState::canStartIonFreeTask,
    2376             :         &HelperThread::handleIonFreeWorkload
    2377             :     },
    2378             :     {
    2379             :         THREAD_TYPE_WASM,
    2380             :         &GlobalHelperThreadState::canStartWasmTier2Compile,
    2381             :         &HelperThread::handleWasmTier2Workload
    2382             :     },
    2383             :     {
    2384             :         THREAD_TYPE_WASM_TIER2,
    2385             :         &GlobalHelperThreadState::canStartWasmTier2Generator,
    2386             :         &HelperThread::handleWasmTier2GeneratorWorkload
    2387             :     }
    2388             : };
    2389             : 
    2390             : void
    2391             : HelperThread::threadLoop()
    2392             : {
    2393           4 :     MOZ_ASSERT(CanUseExtraThreads());
    2394             : 
    2395           0 :     JS::AutoSuppressGCAnalysis nogc;
    2396             :     AutoLockHelperThreadState lock;
    2397           4 : 
    2398           8 :     ensureRegisteredWithProfiler();
    2399             : 
    2400           0 :     JSContext cx(nullptr, JS::ContextOptions());
    2401             :     {
    2402             :         AutoEnterOOMUnsafeRegion oomUnsafe;
    2403             :         if (!cx.init(ContextKind::HelperThread))
    2404             :             oomUnsafe.crash("HelperThread cx.init()");
    2405             :     }
    2406             :     cx.setHelperThread(this);
    2407             :     JS_SetNativeStackQuota(&cx, HELPER_STACK_QUOTA);
    2408             : 
    2409             :     while (!terminate) {
    2410             :         MOZ_ASSERT(idle());
    2411             : 
    2412             :         // The selectors may depend on the HelperThreadState not changing
    2413             :         // between task selection and task execution, in particular, on new
    2414             :         // tasks not being added (because of the lifo structure of the work
    2415             :         // lists). Unlocking the HelperThreadState between task selection and
    2416             :         // execution is not well-defined.
    2417             : 
    2418             :         const TaskSpec* task = findHighestPriorityTask(lock);
    2419             :         if (!task) {
    2420             :             HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
    2421             :             continue;
    2422             :         }
    2423             : 
    2424             :         js::oom::SetThreadType(task->type);
    2425             :         (this->*(task->handleWorkload))(lock);
    2426             :         js::oom::SetThreadType(js::THREAD_TYPE_NONE);
    2427             :     }
    2428             : 
    2429             :     unregisterWithProfilerIfNeeded();
    2430             : }
    2431             : 
    2432             : const HelperThread::TaskSpec*
    2433             : HelperThread::findHighestPriorityTask(const AutoLockHelperThreadState& locked)
    2434             : {
    2435             :     // Return the highest priority task that is ready to start, or nullptr.
    2436             : 
    2437             :     for (const auto& task : taskSpecs) {
    2438             :         if ((HelperThreadState().*(task.canStart))(locked))
    2439             :             return &task;
    2440             :     }
    2441             : 
    2442             :     return nullptr;
    2443             : }

Generated by: LCOV version 1.13-14-ga5dd952