tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HelperThreadState.h (26558B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 /*
      8 * Definitions for managing off-thread work using a process wide list of
      9 * worklist items and pool of threads. Worklist items are engine internal, and
     10 * are distinct from e.g. web workers.
     11 */
     12 
     13 #ifndef vm_HelperThreadState_h
     14 #define vm_HelperThreadState_h
     15 
     16 #include "mozilla/Assertions.h"       // MOZ_ASSERT, MOZ_CRASH
     17 #include "mozilla/Attributes.h"       // MOZ_RAII
     18 #include "mozilla/EnumeratedArray.h"  // mozilla::EnumeratedArray
     19 #include "mozilla/LinkedList.h"  // mozilla::LinkedList, mozilla::LinkedListElement
     20 #include "mozilla/MemoryReporting.h"  // mozilla::MallocSizeOf
     21 #include "mozilla/RefPtr.h"           // RefPtr
     22 #include "mozilla/TimeStamp.h"        // mozilla::TimeDuration
     23 
     24 #include <stddef.h>  // size_t
     25 #include <stdint.h>  // uint32_t, uint64_t
     26 #include <utility>   // std::move
     27 
     28 #include "ds/Fifo.h"                      // Fifo
     29 #include "frontend/CompilationStencil.h"  // frontend::InitialStencilAndDelazifications
     30 #include "gc/GCRuntime.h"                 // gc::GCRuntime
     31 #include "js/AllocPolicy.h"               // SystemAllocPolicy
     32 #include "js/CompileOptions.h"            // JS::ReadOnlyCompileOptions
     33 #include "js/experimental/JSStencil.h"  // JS::InstantiationStorage
     34 #include "js/HelperThreadAPI.h"         // JS::HelperThreadTaskCallback
     35 #include "js/MemoryMetrics.h"           // JS::GlobalStats
     36 #include "js/ProfilingStack.h"  // JS::RegisterThreadCallback, JS::UnregisterThreadCallback
     37 #include "js/RootingAPI.h"                // JS::Handle
     38 #include "js/UniquePtr.h"                 // UniquePtr
     39 #include "js/Utility.h"                   // ThreadType
     40 #include "threading/ConditionVariable.h"  // ConditionVariable
     41 #include "threading/ProtectedData.h"      // WriteOnceData
     42 #include "vm/ConcurrentDelazification.h"  // DelazificationContext
     43 #include "vm/HelperThreads.h"  // AutoLockHelperThreadState, AutoUnlockHelperThreadState
     44 #include "vm/HelperThreadTask.h"             // HelperThreadTask
     45 #include "vm/JSContext.h"                    // JSContext
     46 #include "vm/JSScript.h"                     // ScriptSource
     47 #include "vm/Runtime.h"                      // JSRuntime
     48 #include "vm/SharedImmutableStringsCache.h"  // SharedImmutableString
     49 #include "wasm/WasmConstants.h"              // wasm::CompileState
     50 
     51 class JSTracer;
     52 
     53 namespace js {
     54 
     55 class Compressor;
     56 struct DelazifyTask;
     57 struct FreeDelazifyTask;
     58 struct PromiseHelperTask;
     59 class PromiseObject;
     60 
     61 namespace jit {
     62 class BaselineCompileTask;
     63 class IonCompileTask;
     64 class IonFreeTask;
     65 }  // namespace jit
     66 
     67 namespace wasm {
     68 
     69 struct CompileTask;
     70 using CompileTaskPtrFifo = Fifo<CompileTask*, 0, SystemAllocPolicy>;
     71 
     72 struct CompleteTier2GeneratorTask : public HelperThreadTask {
     73  virtual ~CompleteTier2GeneratorTask() = default;
     74  virtual void cancel() = 0;
     75  const char* getName() override { return "CompleteTier2GeneratorTask"; }
     76 };
     77 
     78 using UniqueCompleteTier2GeneratorTask = UniquePtr<CompleteTier2GeneratorTask>;
     79 using CompleteTier2GeneratorTaskPtrVector =
     80    Vector<CompleteTier2GeneratorTask*, 0, SystemAllocPolicy>;
     81 
     82 struct PartialTier2CompileTask : public HelperThreadTask {
     83  virtual ~PartialTier2CompileTask() = default;
     84  virtual void cancel() = 0;
     85  const char* getName() override { return "PartialTier2CompileTask"; }
     86 };
     87 
     88 using UniquePartialTier2CompileTask = UniquePtr<PartialTier2CompileTask>;
     89 using PartialTier2CompileTaskPtrVector =
     90    Vector<PartialTier2CompileTask*, 0, SystemAllocPolicy>;
     91 
     92 }  // namespace wasm
     93 
     94 // Per-process state for off thread work items.
     95 class GlobalHelperThreadState {
     96 public:
     97  // A single complete tier-2 ModuleGenerator job spawns many compilation jobs,
     98  // and we do not want to allow more than one such ModuleGenerator to run at a
     99  // time.
    100  static const size_t MaxCompleteTier2GeneratorTasks = 1;
    101 
    102  // The number of partial tier 2 compilation tasks that can run
    103  // simultaneously.  This constant specifies unfortunately both the default
    104  // and the maximum.
    105  static const size_t MaxPartialTier2CompileTasks = 1;
    106 
    107  // Number of CPUs to treat this machine as having when creating threads.
    108  // May be accessed without locking.
    109  size_t cpuCount;
    110 
    111  // Number of threads to create. May be accessed without locking.
    112  size_t threadCount;
    113 
    114  // Thread stack quota to use when running tasks.
    115  size_t stackQuota;
    116 
    117  bool terminating_ = false;
    118 
    119  using BaselineCompileTaskVector =
    120      Vector<jit::BaselineCompileTask*, 1, SystemAllocPolicy>;
    121  using IonCompileTaskVector =
    122      Vector<jit::IonCompileTask*, 0, SystemAllocPolicy>;
    123  using IonFreeTaskVector =
    124      Vector<js::UniquePtr<jit::IonFreeTask>, 0, SystemAllocPolicy>;
    125  using DelazifyTaskList = mozilla::LinkedList<DelazifyTask>;
    126  using FreeDelazifyTaskVector =
    127      Vector<js::UniquePtr<FreeDelazifyTask>, 1, SystemAllocPolicy>;
    128  using SourceCompressionTaskVector =
    129      Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy>;
    130  using PromiseHelperTaskVector =
    131      Vector<PromiseHelperTask*, 0, SystemAllocPolicy>;
    132 
    133  // Count of running task by each threadType.
    134  mozilla::EnumeratedArray<ThreadType, size_t,
    135                           size_t(ThreadType::THREAD_TYPE_MAX)>
    136      runningTaskCount;
    137  size_t totalCountRunningTasks;
    138 
    139  WriteOnceData<JS::RegisterThreadCallback> registerThread;
    140  WriteOnceData<JS::UnregisterThreadCallback> unregisterThread;
    141 
    142  // Count of helper threads 'reserved' for parallel marking. This is used to
    143  // prevent too many runtimes trying to mark in parallel at once. Does not stop
    144  // threads from being used for other kinds of task, including GC tasks.
    145  HelperThreadLockData<size_t> gcParallelMarkingThreads;
    146 
    147 private:
    148  // The lists below are all protected by |lock|.
    149 
    150  // Baseline compilation worklist and finished jobs.
    151  BaselineCompileTaskVector baselineWorklist_, baselineFinishedList_;
    152 
    153  // Ion compilation worklist and finished jobs.
    154  IonCompileTaskVector ionWorklist_, ionFinishedList_;
    155  IonFreeTaskVector ionFreeList_;
    156 
    157  // wasm worklists.
    158  wasm::CompileTaskPtrFifo wasmWorklist_tier1_;
    159  wasm::CompileTaskPtrFifo wasmWorklist_tier2_;
    160  wasm::CompleteTier2GeneratorTaskPtrVector wasmCompleteTier2GeneratorWorklist_;
    161  wasm::PartialTier2CompileTaskPtrVector wasmPartialTier2CompileWorklist_;
    162 
    163  // Count of finished CompleteTier2Generator tasks.
    164  uint32_t wasmCompleteTier2GeneratorsFinished_;
    165 
    166  // Async tasks that, upon completion, are dispatched back to the JSContext's
    167  // owner thread via embedding callbacks instead of a finished list.
    168  PromiseHelperTaskVector promiseHelperTasks_;
    169 
    170  // Script worklist, which might still have function to delazify.
    171  DelazifyTaskList delazifyWorklist_;
    172  // Ideally an instance should not have a method to free it-self as, the method
    173  // has a this pointer, which aliases the deleted instance, and that the method
    174  // might have some of its fields aliased on the stack.
    175  //
    176  // Delazification task are complex and have a lot of fields. To reduce the
    177  // risk of having aliased fields on the stack while deleting instances of a
    178  // DelazifyTask, we have FreeDelazifyTask. While FreeDelazifyTask suffer from
    179  // the same problem, the limited scope of their actions should mitigate the
    180  // risk.
    181  FreeDelazifyTaskVector freeDelazifyTaskVector_;
    182 
    183  // Source compression worklist of tasks that can start.
    184  SourceCompressionTaskVector compressionWorklist_;
    185 
    186  // Finished source compression tasks.
    187  SourceCompressionTaskVector compressionFinishedList_;
    188 
    189  // GC tasks needing to be done in parallel. These are first queued in the
    190  // GCRuntime before being dispatched to the helper thread system.
    191  GCParallelTaskList gcParallelWorklist_;
    192 
    193  using HelperThreadTaskVector =
    194      Vector<HelperThreadTask*, 0, SystemAllocPolicy>;
    195  // Vector of running HelperThreadTask.
    196  // This is used to get the HelperThreadTask that are currently running.
    197  HelperThreadTaskVector helperTasks_;
    198 
    199  // Callback to dispatch a task to a thread pool. Set by
    200  // JS::SetHelperThreadTaskCallback. If this is not set the internal thread
    201  // pool is used.
    202  JS::HelperThreadTaskCallback dispatchTaskCallback = nullptr;
    203  friend class AutoHelperTaskQueue;
    204 
    205  // Condition variable for notifiying the main thread that a helper task has
    206  // completed some work.
    207  js::ConditionVariable consumerWakeup;
    208 
    209 #ifdef DEBUG
    210  // The number of tasks dispatched to the thread pool that have not started
    211  // running yet.
    212  size_t tasksPending_ = 0;
    213 #endif
    214 
    215  bool isInitialized_ = false;
    216 
    217  bool useInternalThreadPool_ = true;
    218 
    219 public:
    220  void addSizeOfIncludingThis(JS::GlobalStats* stats,
    221                              const AutoLockHelperThreadState& lock) const;
    222 
    223  size_t maxBaselineCompilationThreads() const;
    224  size_t maxIonCompilationThreads() const;
    225  size_t maxIonFreeThreads() const;
    226  size_t maxWasmCompilationThreads() const;
    227  size_t maxWasmCompleteTier2GeneratorThreads() const;
    228  size_t maxWasmPartialTier2CompileThreads() const;
    229  size_t maxPromiseHelperThreads() const;
    230  size_t maxDelazifyThreads() const;
    231  size_t maxCompressionThreads() const;
    232  size_t maxGCParallelThreads() const;
    233 
    234  GlobalHelperThreadState();
    235 
    236  bool isInitialized(const AutoLockHelperThreadState& lock) const {
    237    return isInitialized_;
    238  }
    239 
    240  [[nodiscard]] bool ensureInitialized();
    241  [[nodiscard]] bool ensureThreadCount(size_t count,
    242                                       AutoLockHelperThreadState& lock);
    243  void finish(AutoLockHelperThreadState& lock);
    244  void finishThreads(AutoLockHelperThreadState& lock);
    245 
    246  void setCpuCount(size_t count);
    247 
    248  void setDispatchTaskCallback(JS::HelperThreadTaskCallback callback,
    249                               size_t threadCount, size_t stackSize,
    250                               const AutoLockHelperThreadState& lock);
    251 
    252  void destroyHelperContexts(AutoLockHelperThreadState& lock);
    253 
    254 #ifdef DEBUG
    255  void assertIsLockedByCurrentThread() const;
    256 #endif
    257 
    258  void wait(AutoLockHelperThreadState& lock,
    259            mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever());
    260  void notifyAll(const AutoLockHelperThreadState&);
    261 
    262  bool useInternalThreadPool(const AutoLockHelperThreadState& lock) const {
    263    return useInternalThreadPool_;
    264  }
    265 
    266  bool isTerminating(const AutoLockHelperThreadState& locked) const {
    267    return terminating_;
    268  }
    269 
    270 private:
    271  void notifyOne(const AutoLockHelperThreadState&);
    272 
    273 public:
    274  // Helper method for removing items from the vectors below while iterating
    275  // over them.
    276  template <typename T>
    277  static void remove(T& vector, size_t* index) {
    278    // Self-moving is undefined behavior.
    279    if (*index != vector.length() - 1) {
    280      vector[*index] = std::move(vector.back());
    281    }
    282    (*index)--;
    283    vector.popBack();
    284  }
    285 
    286  BaselineCompileTaskVector& baselineWorklist(
    287      const AutoLockHelperThreadState&) {
    288    return baselineWorklist_;
    289  }
    290  BaselineCompileTaskVector& baselineFinishedList(
    291      const AutoLockHelperThreadState&) {
    292    return baselineFinishedList_;
    293  }
    294  IonCompileTaskVector& ionWorklist(const AutoLockHelperThreadState&) {
    295    return ionWorklist_;
    296  }
    297  IonCompileTaskVector& ionFinishedList(const AutoLockHelperThreadState&) {
    298    return ionFinishedList_;
    299  }
    300  IonFreeTaskVector& ionFreeList(const AutoLockHelperThreadState&) {
    301    return ionFreeList_;
    302  }
    303 
    304  wasm::CompileTaskPtrFifo& wasmWorklist(const AutoLockHelperThreadState&,
    305                                         wasm::CompileState state) {
    306    switch (state) {
    307      case wasm::CompileState::Once:
    308      case wasm::CompileState::EagerTier1:
    309      case wasm::CompileState::LazyTier1:
    310        return wasmWorklist_tier1_;
    311      case wasm::CompileState::EagerTier2:
    312      case wasm::CompileState::LazyTier2:
    313        return wasmWorklist_tier2_;
    314      default:
    315        MOZ_CRASH();
    316    }
    317  }
    318 
    319  wasm::CompleteTier2GeneratorTaskPtrVector& wasmCompleteTier2GeneratorWorklist(
    320      const AutoLockHelperThreadState&) {
    321    return wasmCompleteTier2GeneratorWorklist_;
    322  }
    323 
    324  wasm::PartialTier2CompileTaskPtrVector& wasmPartialTier2CompileWorklist(
    325      const AutoLockHelperThreadState&) {
    326    return wasmPartialTier2CompileWorklist_;
    327  }
    328 
    329  void incWasmCompleteTier2GeneratorsFinished(
    330      const AutoLockHelperThreadState&) {
    331    wasmCompleteTier2GeneratorsFinished_++;
    332  }
    333 
    334  uint32_t wasmCompleteTier2GeneratorsFinished(
    335      const AutoLockHelperThreadState&) const {
    336    return wasmCompleteTier2GeneratorsFinished_;
    337  }
    338 
    339  PromiseHelperTaskVector& promiseHelperTasks(
    340      const AutoLockHelperThreadState&) {
    341    return promiseHelperTasks_;
    342  }
    343 
    344  DelazifyTaskList& delazifyWorklist(const AutoLockHelperThreadState&) {
    345    return delazifyWorklist_;
    346  }
    347 
    348  FreeDelazifyTaskVector& freeDelazifyTaskVector(
    349      const AutoLockHelperThreadState&) {
    350    return freeDelazifyTaskVector_;
    351  }
    352 
    353  SourceCompressionTaskVector& compressionWorklist(
    354      const AutoLockHelperThreadState&) {
    355    return compressionWorklist_;
    356  }
    357 
    358  SourceCompressionTaskVector& compressionFinishedList(
    359      const AutoLockHelperThreadState&) {
    360    return compressionFinishedList_;
    361  }
    362 
    363 private:
    364  GCParallelTaskList& gcParallelWorklist() { return gcParallelWorklist_; }
    365 
    366  HelperThreadTaskVector& helperTasks(const AutoLockHelperThreadState&) {
    367    return helperTasks_;
    368  }
    369 
    370  bool canStartWasmCompile(const AutoLockHelperThreadState& lock,
    371                           wasm::CompileState state);
    372 
    373  bool canStartWasmTier1CompileTask(const AutoLockHelperThreadState& lock);
    374  bool canStartWasmTier2CompileTask(const AutoLockHelperThreadState& lock);
    375  bool canStartWasmCompleteTier2GeneratorTask(
    376      const AutoLockHelperThreadState& lock);
    377  bool canStartWasmPartialTier2CompileTask(
    378      const AutoLockHelperThreadState& lock);
    379  bool canStartPromiseHelperTask(const AutoLockHelperThreadState& lock);
    380  bool canStartBaselineCompileTask(const AutoLockHelperThreadState& lock);
    381  bool canStartIonCompileTask(const AutoLockHelperThreadState& lock);
    382  bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
    383  bool canStartFreeDelazifyTask(const AutoLockHelperThreadState& lock);
    384  bool canStartDelazifyTask(const AutoLockHelperThreadState& lock);
    385  bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
    386  bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
    387 
    388  HelperThreadTask* maybeGetWasmCompile(const AutoLockHelperThreadState& lock,
    389                                        wasm::CompileState state);
    390 
    391  HelperThreadTask* maybeGetWasmTier1CompileTask(
    392      const AutoLockHelperThreadState& lock);
    393  HelperThreadTask* maybeGetWasmTier2CompileTask(
    394      const AutoLockHelperThreadState& lock);
    395  HelperThreadTask* maybeGetWasmCompleteTier2GeneratorTask(
    396      const AutoLockHelperThreadState& lock);
    397  HelperThreadTask* maybeGetWasmPartialTier2CompileTask(
    398      const AutoLockHelperThreadState& lock);
    399  HelperThreadTask* maybeGetPromiseHelperTask(
    400      const AutoLockHelperThreadState& lock);
    401  HelperThreadTask* maybeGetBaselineCompileTask(
    402      const AutoLockHelperThreadState& lock);
    403  HelperThreadTask* maybeGetIonCompileTask(
    404      const AutoLockHelperThreadState& lock);
    405  HelperThreadTask* maybeGetLowPrioIonCompileTask(
    406      const AutoLockHelperThreadState& lock);
    407  HelperThreadTask* maybeGetIonFreeTask(const AutoLockHelperThreadState& lock);
    408  HelperThreadTask* maybeGetFreeDelazifyTask(
    409      const AutoLockHelperThreadState& lock);
    410  HelperThreadTask* maybeGetDelazifyTask(const AutoLockHelperThreadState& lock);
    411  HelperThreadTask* maybeGetCompressionTask(
    412      const AutoLockHelperThreadState& lock);
    413  HelperThreadTask* maybeGetGCParallelTask(
    414      const AutoLockHelperThreadState& lock);
    415 
    416  jit::IonCompileTask* highestPriorityPendingIonCompile(
    417      const AutoLockHelperThreadState& lock, bool checkExecutionStatus);
    418 
    419  bool checkTaskThreadLimit(ThreadType threadType, size_t maxThreads,
    420                            bool isMaster,
    421                            const AutoLockHelperThreadState& lock) const;
    422  bool checkTaskThreadLimit(ThreadType threadType, size_t maxThreads,
    423                            const AutoLockHelperThreadState& lock) const {
    424    return checkTaskThreadLimit(threadType, maxThreads, /* isMaster */ false,
    425                                lock);
    426  }
    427 
    428  bool hasActiveThreads(const AutoLockHelperThreadState&);
    429  bool canStartTasks(const AutoLockHelperThreadState& locked);
    430 
    431 public:
    432  // Used by a major GC to create and enqueue compression tasks.
    433  enum class ScheduleCompressionTask { NonShrinkingGC, ShrinkingGC, API };
    434  void createAndSubmitCompressionTasks(ScheduleCompressionTask schedule,
    435                                       JSRuntime* rt);
    436 
    437  void runPendingSourceCompressions(JSRuntime* runtime);
    438 
    439  void trace(JSTracer* trc);
    440 
    441  void waitForAllTasks();
    442  void waitForAllTasksLocked(AutoLockHelperThreadState&);
    443 
    444 #ifdef DEBUG
    445  bool hasOffThreadIonCompile(Zone* zone, AutoLockHelperThreadState& lock);
    446 #endif
    447 
    448  void cancelOffThreadBaselineCompile(const CompilationSelector& selector);
    449  void cancelOffThreadIonCompile(const CompilationSelector& selector);
    450  void cancelOffThreadWasmCompleteTier2Generator(
    451      AutoLockHelperThreadState& lock);
    452  void cancelOffThreadWasmPartialTier2Compile(AutoLockHelperThreadState& lock);
    453 
    454  bool hasAnyDelazifyTask(JSRuntime* rt, AutoLockHelperThreadState& lock);
    455  void cancelPendingDelazifyTask(JSRuntime* rt,
    456                                 AutoLockHelperThreadState& lock);
    457  void waitUntilCancelledDelazifyTasks(JSRuntime* rt,
    458                                       AutoLockHelperThreadState& lock);
    459  void waitUntilEmptyFreeDelazifyTaskVector(AutoLockHelperThreadState& lock);
    460 
    461  void cancelOffThreadCompressions(JSRuntime* runtime,
    462                                   AutoLockHelperThreadState& lock);
    463 
    464  void triggerFreeUnusedMemory();
    465 
    466  bool submitTask(wasm::UniqueCompleteTier2GeneratorTask task);
    467  bool submitTask(wasm::UniquePartialTier2CompileTask task);
    468  bool submitTask(wasm::CompileTask* task, wasm::CompileState state);
    469  bool submitTask(jit::BaselineCompileTask* task,
    470                  const AutoLockHelperThreadState& locked);
    471  bool submitTask(UniquePtr<jit::IonFreeTask>&& task,
    472                  const AutoLockHelperThreadState& lock);
    473  bool submitTask(jit::IonCompileTask* task,
    474                  const AutoLockHelperThreadState& locked);
    475  bool submitTask(UniquePtr<SourceCompressionTask> task,
    476                  const AutoLockHelperThreadState& locked);
    477  void submitTask(DelazifyTask* task, const AutoLockHelperThreadState& locked);
    478  bool submitTask(UniquePtr<FreeDelazifyTask> task,
    479                  const AutoLockHelperThreadState& locked);
    480  bool submitTask(PromiseHelperTask* task);
    481  bool submitTask(GCParallelTask* task,
    482                  const AutoLockHelperThreadState& locked);
    483 
    484  void runOneTask(HelperThreadTask* task, AutoLockHelperThreadState& lock);
    485  void dispatch(const AutoLockHelperThreadState& locked);
    486 
    487 private:
    488  HelperThreadTask* findHighestPriorityTask(
    489      const AutoLockHelperThreadState& locked);
    490 
    491  void runTaskLocked(HelperThreadTask* task, AutoLockHelperThreadState& lock);
    492 
    493  using Selector = HelperThreadTask* (
    494      GlobalHelperThreadState::*)(const AutoLockHelperThreadState&);
    495  static const Selector selectors[];
    496 };
    497 
    498 static inline bool IsHelperThreadStateInitialized() {
    499  extern GlobalHelperThreadState* gHelperThreadState;
    500  return gHelperThreadState;
    501 }
    502 
    503 static inline GlobalHelperThreadState& HelperThreadState() {
    504  extern GlobalHelperThreadState* gHelperThreadState;
    505 
    506  MOZ_ASSERT(gHelperThreadState);
    507  return *gHelperThreadState;
    508 }
    509 
    510 // Eagerly delazify functions, and send the result back to the runtime which
    511 // requested the stencil to be parsed, by filling the stencil cache.
    512 //
    513 // This task is scheduled multiple times, each time it is scheduled, it
    514 // delazifies a single function. Once the function is delazified, it schedules
    515 // the inner functions of the delazified function for delazification using the
    516 // DelazifyStrategy. The DelazifyStrategy is responsible for ordering and
    517 // filtering functions to be delazified.
    518 //
    519 // When no more function have to be delazified, a FreeDelazifyTask is scheduled
    520 // to remove the memory held by the DelazifyTask.
    521 struct DelazifyTask : public mozilla::LinkedListElement<DelazifyTask>,
    522                      public HelperThreadTask {
    523  // HelperThreads are shared between all runtimes in the process so explicitly
    524  // track which one we are associated with.
    525  JSRuntime* maybeRuntime = nullptr;
    526 
    527  DelazificationContext delazificationCx;
    528 
    529  // Create a new DelazifyTask and initialize it.
    530  //
    531  // In case of early failure, no errors are reported, as a DelazifyTask is an
    532  // optimization and the VM should remain working even without this
    533  // optimization in place.
    534  static UniquePtr<DelazifyTask> Create(
    535      JSRuntime* maybeRuntime, const JS::ReadOnlyCompileOptions& options,
    536      frontend::InitialStencilAndDelazifications* stencils);
    537 
    538  DelazifyTask(JSRuntime* maybeRuntime,
    539               const JS::PrefableCompileOptions& initialPrefableOptions);
    540  ~DelazifyTask();
    541 
    542  [[nodiscard]] bool init(const JS::ReadOnlyCompileOptions& options,
    543                          frontend::InitialStencilAndDelazifications* stencils);
    544 
    545  bool runtimeMatchesOrNoRuntime(JSRuntime* rt) {
    546    return !maybeRuntime || maybeRuntime == rt;
    547  }
    548 
    549  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
    550  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    551    return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
    552  }
    553 
    554  void runHelperThreadTask(AutoLockHelperThreadState& locked) override;
    555  [[nodiscard]] bool runTask();
    556  ThreadType threadType() override { return ThreadType::THREAD_TYPE_DELAZIFY; }
    557 
    558  bool done() const;
    559 
    560  const char* getName() override { return "DelazifyTask"; }
    561 };
    562 
    563 // The FreeDelazifyTask exists as this is a bad practice to `js_delete(this)`,
    564 // as fields might be aliased across the destructor, such as with RAII guards.
    565 // The FreeDelazifyTask limits the risk of adding these kind of issues by
    566 // limiting the number of fields to the DelazifyTask pointer, before deleting
    567 // it-self.
    568 struct FreeDelazifyTask : public HelperThreadTask {
    569  DelazifyTask* task;
    570 
    571  explicit FreeDelazifyTask(DelazifyTask* t) : task(t) {}
    572  void runHelperThreadTask(AutoLockHelperThreadState& locked) override;
    573  ThreadType threadType() override {
    574    return ThreadType::THREAD_TYPE_DELAZIFY_FREE;
    575  }
    576 
    577  const char* getName() override { return "FreeDelazifyTask"; }
    578 };
    579 
    580 // Entry for a single ScriptSource in a SourceCompressionTask.
    581 class SourceCompressionTaskEntry {
    582  // The source to be compressed.
    583  RefPtr<ScriptSource> source_;
    584 
    585  // The resultant compressed string. If the compressed string is larger
    586  // than the original, or we OOM'd during compression, or nothing else
    587  // except the task is holding the ScriptSource alive when scheduled to
    588  // compress, this will remain None upon completion.
    589  SharedImmutableString resultString_;
    590 
    591 public:
    592  explicit SourceCompressionTaskEntry(ScriptSource* source) : source_(source) {}
    593 
    594  bool shouldCancel() const {
    595    // If the refcount is exactly 1, then nothing else is holding on to the
    596    // ScriptSource, so no reason to compress it and we should cancel the task.
    597    return source_->refs == 1;
    598  }
    599 
    600  // The work algorithm, aware whether it's compressing one-byte UTF-8 source
    601  // text or UTF-16, for CharT either Utf8Unit or char16_t.  Invoked by
    602  // work() after doing a type-test of the ScriptSource*.
    603  template <typename CharT>
    604  void workEncodingSpecific(Compressor& comp);
    605 
    606  void runTask(Compressor& comp);
    607  void complete();
    608 
    609  struct PerformTaskWork;
    610  friend struct PerformTaskWork;
    611 };
    612 
    613 // Off-thread task for compressing one or more script sources.
    614 //
    615 // Completed tasks are handled during the sweeping phase by
    616 // AttachFinishedCompressions, which runs in parallel with other GC sweeping
    617 // tasks.
    618 class SourceCompressionTask final : public HelperThreadTask {
    619  friend class HelperThread;
    620  friend class ScriptSource;
    621 
    622  // The runtime that the task is associated with, in the sense that it uses the
    623  // runtime's immutable string cache.
    624  JSRuntime* runtime_;
    625 
    626  // The script sources to compress.
    627  Vector<SourceCompressionTaskEntry, 4, SystemAllocPolicy> entries_;
    628 
    629 public:
    630  SourceCompressionTask(JSRuntime* rt, ScriptSource* source) : runtime_(rt) {
    631    static_assert(decltype(entries_)::InlineLength >= 1,
    632                  "Appending one entry should be infallible");
    633    MOZ_ALWAYS_TRUE(entries_.emplaceBack(source));
    634  }
    635  virtual ~SourceCompressionTask() = default;
    636 
    637  bool runtimeMatches(JSRuntime* runtime) const { return runtime == runtime_; }
    638 
    639  [[nodiscard]] bool addEntry(ScriptSource* source) {
    640    return entries_.emplaceBack(source);
    641  }
    642 
    643  void runTask();
    644  void runHelperThreadTask(AutoLockHelperThreadState& locked) override;
    645  void complete();
    646 
    647  ThreadType threadType() override { return ThreadType::THREAD_TYPE_COMPRESS; }
    648 
    649  const char* getName() override { return "SourceCompressionTask"; }
    650 };
    651 
    652 // A PromiseHelperTask is an OffThreadPromiseTask that executes a single job on
    653 // a helper thread. Call js::StartOffThreadPromiseHelperTask to submit a
    654 // PromiseHelperTask for execution.
    655 //
    656 // Concrete subclasses must implement execute and OffThreadPromiseTask::resolve.
    657 // The helper thread will call execute() to do the main work. Then, the thread
    658 // of the JSContext used to create the PromiseHelperTask will call resolve() to
    659 // resolve promise according to those results.
    660 struct PromiseHelperTask : OffThreadPromiseTask, public HelperThreadTask {
    661  PromiseHelperTask(JSContext* cx, JS::Handle<PromiseObject*> promise)
    662      : OffThreadPromiseTask(cx, promise) {}
    663 
    664  // To be called on a helper thread and implemented by the derived class.
    665  virtual void execute() = 0;
    666 
    667  // May be called in the absence of helper threads or off-thread promise
    668  // support to synchronously execute and resolve a PromiseTask.
    669  //
    670  // Warning: After this function returns, 'this' can be deleted at any time, so
    671  // the caller must immediately return from the stream callback.
    672  void executeAndResolveAndDestroy(JSContext* cx);
    673 
    674  void runHelperThreadTask(AutoLockHelperThreadState& locked) override;
    675  ThreadType threadType() override { return THREAD_TYPE_PROMISE_TASK; }
    676 
    677  const char* getName() override { return "PromiseHelperTask"; }
    678 };
    679 
    680 } /* namespace js */
    681 
    682 #endif /* vm_HelperThreadState_h */