/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * API for managing off-thread work. */ #ifndef vm_HelperThreads_h #define vm_HelperThreads_h #include "mozilla/Variant.h" #include "js/AllocPolicy.h" #include "js/HelperThreadAPI.h" #include "js/shadow/Zone.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "threading/LockGuard.h" #include "threading/Mutex.h" #include "wasm/WasmConstants.h" namespace mozilla { union Utf8Unit; } namespace JS { class JS_PUBLIC_API ReadOnlyCompileOptions; class JS_PUBLIC_API ReadOnlyDecodeOptions; class Zone; template class SourceText; } // namespace JS namespace js { class AutoLockHelperThreadState; struct PromiseHelperTask; class SourceCompressionTask; namespace frontend { struct CompilationStencil; } namespace gc { class GCRuntime; } namespace jit { class IonCompileTask; class IonFreeTask; class JitRuntime; using IonFreeCompileTasks = Vector; } // namespace jit namespace wasm { struct CompileTask; struct CompileTaskState; struct Tier2GeneratorTask; using UniqueTier2GeneratorTask = UniquePtr; } // namespace wasm /* * Lock protecting all mutable shared state accessed by helper threads, and used * by all condition variables. */ extern Mutex gHelperThreadLock MOZ_UNANNOTATED; // Set of tasks to dispatch when the helper thread state lock is released. class AutoHelperTaskQueue { public: ~AutoHelperTaskQueue() { dispatchQueuedTasks(); } bool hasQueuedTasks() const { return newTasksToDispatch || finishedTasksToDispatch; } void queueTaskToDispatch(JS::DispatchReason reason) const; void dispatchQueuedTasks(); private: mutable uint32_t newTasksToDispatch = 0; mutable uint32_t finishedTasksToDispatch = 0; }; // A lock guard for data protected by the helper thread lock. // // This can also queue helper thread tasks to be triggered when the lock is // released. class MOZ_RAII AutoLockHelperThreadState : public AutoHelperTaskQueue, // Must come before LockGuard. public LockGuard { public: AutoLockHelperThreadState() : LockGuard(gHelperThreadLock) {} AutoLockHelperThreadState(const AutoLockHelperThreadState&) = delete; private: friend class UnlockGuard; void unlock() { LockGuard::unlock(); dispatchQueuedTasks(); } friend class GlobalHelperThreadState; }; using AutoUnlockHelperThreadState = UnlockGuard; // Create data structures used by helper threads. bool CreateHelperThreadsState(); // Destroy data structures used by helper threads. void DestroyHelperThreadsState(); // Initialize helper threads unless already initialized. bool EnsureHelperThreadsInitialized(); size_t GetHelperThreadCount(); size_t GetHelperThreadCPUCount(); size_t GetMaxWasmCompilationThreads(); // This allows the JS shell to override GetCPUCount() when passed the // --thread-count=N option. bool SetFakeCPUCount(size_t count); // Enqueues a wasm compilation task. bool StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode); // Remove any pending wasm compilation tasks queued with // StartOffThreadWasmCompile that match the arguments. Return the number // removed. size_t RemovePendingWasmCompileTasks(const wasm::CompileTaskState& taskState, wasm::CompileMode mode, const AutoLockHelperThreadState& lock); // Enqueues a wasm compilation task. void StartOffThreadWasmTier2Generator(wasm::UniqueTier2GeneratorTask task); // Cancel all background Wasm Tier-2 compilations. void CancelOffThreadWasmTier2Generator(); /* * If helper threads are available, call execute() then dispatchResolve() on the * given task in a helper thread. If no helper threads are available, the given * task is executed and resolved synchronously. * * This function takes ownership of task unconditionally; if it fails, task is * deleted. */ bool StartOffThreadPromiseHelperTask(JSContext* cx, UniquePtr task); /* * Like the JSContext-accepting version, but only safe to use when helper * threads are available, so we can be sure we'll never need to fall back on * synchronous execution. * * This function can be called from any thread, but takes ownership of the task * only on success. On OOM, it is the caller's responsibility to arrange for the * task to be cleaned up properly. */ bool StartOffThreadPromiseHelperTask(PromiseHelperTask* task); /* * Schedule an off-thread Ion compilation for a script, given a task. */ bool StartOffThreadIonCompile(jit::IonCompileTask* task, const AutoLockHelperThreadState& lock); void FinishOffThreadIonCompile(jit::IonCompileTask* task, const AutoLockHelperThreadState& lock); // RAII class to handle batching compile tasks and starting an IonFreeTask. class MOZ_RAII AutoStartIonFreeTask { jit::JitRuntime* jitRuntime_; // If true, start an IonFreeTask even if the batch is small. bool force_; public: explicit AutoStartIonFreeTask(jit::JitRuntime* jitRuntime, bool force = false) : jitRuntime_(jitRuntime), force_(force) {} ~AutoStartIonFreeTask(); [[nodiscard]] bool addIonCompileToFreeTaskBatch(jit::IonCompileTask* task); }; struct ZonesInState { JSRuntime* runtime; JS::shadow::Zone::GCState state; }; using CompilationSelector = mozilla::Variant; /* * Cancel scheduled or in progress Ion compilations. */ void CancelOffThreadIonCompile(const CompilationSelector& selector); inline void CancelOffThreadIonCompile(JSScript* script) { CancelOffThreadIonCompile(CompilationSelector(script)); } inline void CancelOffThreadIonCompile(JS::Zone* zone) { CancelOffThreadIonCompile(CompilationSelector(zone)); } inline void CancelOffThreadIonCompile(JSRuntime* runtime, JS::shadow::Zone::GCState state) { CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state})); } inline void CancelOffThreadIonCompile(JSRuntime* runtime) { CancelOffThreadIonCompile(CompilationSelector(runtime)); } #ifdef DEBUG bool HasOffThreadIonCompile(JS::Zone* zone); #endif /* * Cancel all scheduled or in progress eager delazification phases for a * runtime. */ void CancelOffThreadDelazify(JSRuntime* runtime); /* * Wait for all delazification to complete. */ void WaitForAllDelazifyTasks(JSRuntime* rt); // Start off-thread delazification task, to race the delazification of inner // functions. void StartOffThreadDelazification(JSContext* maybeCx, const JS::ReadOnlyCompileOptions& options, const frontend::CompilationStencil& stencil); // Drain the task queues and wait for all helper threads to finish running. // // Note that helper threads are shared between runtimes and it's possible that // another runtime could saturate the helper thread system and cause this to // never return. void WaitForAllHelperThreads(); void WaitForAllHelperThreads(AutoLockHelperThreadState& lock); // Enqueue a compression job to be processed later. These are started at the // start of the major GC after the next one. bool EnqueueOffThreadCompression(JSContext* cx, UniquePtr task); // Start handling any compression tasks for this runtime. Called at the start of // major GC. void StartHandlingCompressionsOnGC(JSRuntime* rt); // Cancel all scheduled, in progress, or finished compression tasks for // runtime. void CancelOffThreadCompressions(JSRuntime* runtime); void AttachFinishedCompressions(JSRuntime* runtime, AutoLockHelperThreadState& lock); // Sweep pending tasks that are holding onto should-be-dead ScriptSources. void SweepPendingCompressions(AutoLockHelperThreadState& lock); // Run all pending source compression tasks synchronously, for testing purposes void RunPendingSourceCompressions(JSRuntime* runtime); // False if the off-thread source compression mechanism isn't being used. This // happens on low core count machines where we are concerned about blocking // main-thread execution. bool IsOffThreadSourceCompressionEnabled(); } // namespace js #endif /* vm_HelperThreads_h */