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 : /*
8 : * Definitions for managing off-thread work using a process wide list
9 : * of worklist items and pool of threads. Worklist items are engine internal,
10 : * and are distinct from e.g. web workers.
11 : */
12 :
13 : #ifndef vm_HelperThreads_h
14 : #define vm_HelperThreads_h
15 :
16 : #include "mozilla/Attributes.h"
17 : #include "mozilla/GuardObjects.h"
18 : #include "mozilla/PodOperations.h"
19 : #include "mozilla/TimeStamp.h"
20 : #include "mozilla/TypeTraits.h"
21 : #include "mozilla/Variant.h"
22 :
23 : #include "jsapi.h"
24 :
25 : #include "ds/Fifo.h"
26 : #include "jit/Ion.h"
27 : #include "js/TypeDecls.h"
28 : #include "threading/ConditionVariable.h"
29 : #include "vm/JSContext.h"
30 : #include "vm/MutexIDs.h"
31 :
32 : namespace JS {
33 : class OffThreadToken {};
34 : } // namespace JS
35 :
36 : namespace js {
37 :
38 : class AutoLockHelperThreadState;
39 : class AutoUnlockHelperThreadState;
40 : class CompileError;
41 : struct HelperThread;
42 : struct ParseTask;
43 : struct PromiseHelperTask;
44 : namespace jit {
45 : class IonBuilder;
46 : } // namespace jit
47 : namespace wasm {
48 : struct Tier2GeneratorTask;
49 : } // namespace wasm
50 :
51 : enum class ParseTaskKind
52 : {
53 : Script,
54 : Module,
55 : ScriptDecode,
56 : BinAST,
57 : MultiScriptsDecode
58 : };
59 :
60 : namespace wasm {
61 :
62 : struct CompileTask;
63 : typedef Fifo<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrFifo;
64 :
65 : struct Tier2GeneratorTask
66 : {
67 : virtual ~Tier2GeneratorTask() = default;
68 : virtual void cancel() = 0;
69 : virtual void execute() = 0;
70 : };
71 :
72 : typedef UniquePtr<Tier2GeneratorTask> UniqueTier2GeneratorTask;
73 : typedef Vector<Tier2GeneratorTask*, 0, SystemAllocPolicy> Tier2GeneratorTaskPtrVector;
74 :
75 : } // namespace wasm
76 :
77 : // Per-process state for off thread work items.
78 0 : class GlobalHelperThreadState
79 : {
80 : friend class AutoLockHelperThreadState;
81 : friend class AutoUnlockHelperThreadState;
82 :
83 : public:
84 : // A single tier-2 ModuleGenerator job spawns many compilation jobs, and we
85 : // do not want to allow more than one such ModuleGenerator to run at a time.
86 : static const size_t MaxTier2GeneratorTasks = 1;
87 :
88 : // Number of CPUs to treat this machine as having when creating threads.
89 : // May be accessed without locking.
90 : size_t cpuCount;
91 :
92 : // Number of threads to create. May be accessed without locking.
93 : size_t threadCount;
94 :
95 : typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
96 : typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
97 : typedef mozilla::LinkedList<ParseTask> ParseTaskList;
98 : typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector;
99 : typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
100 : typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
101 : typedef Vector<PromiseHelperTask*, 0, SystemAllocPolicy> PromiseHelperTaskVector;
102 :
103 : // List of available threads, or null if the thread state has not been initialized.
104 : using HelperThreadVector = Vector<HelperThread, 0, SystemAllocPolicy>;
105 : UniquePtr<HelperThreadVector> threads;
106 :
107 : WriteOnceData<JS::RegisterThreadCallback> registerThread;
108 : WriteOnceData<JS::UnregisterThreadCallback> unregisterThread;
109 :
110 : private:
111 : // The lists below are all protected by |lock|.
112 :
113 : // Ion compilation worklist and finished jobs.
114 : IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_;
115 :
116 : // wasm worklists.
117 : wasm::CompileTaskPtrFifo wasmWorklist_tier1_;
118 : wasm::CompileTaskPtrFifo wasmWorklist_tier2_;
119 : wasm::Tier2GeneratorTaskPtrVector wasmTier2GeneratorWorklist_;
120 :
121 : // Count of finished Tier2Generator tasks.
122 : uint32_t wasmTier2GeneratorsFinished_;
123 :
124 : // Async tasks that, upon completion, are dispatched back to the JSContext's
125 : // owner thread via embedding callbacks instead of a finished list.
126 : PromiseHelperTaskVector promiseHelperTasks_;
127 :
128 : // Script parsing/emitting worklist and finished jobs.
129 : ParseTaskVector parseWorklist_;
130 : ParseTaskList parseFinishedList_;
131 :
132 : // Parse tasks waiting for an atoms-zone GC to complete.
133 : ParseTaskVector parseWaitingOnGC_;
134 :
135 : // Source compression worklist of tasks that we do not yet know can start.
136 : SourceCompressionTaskVector compressionPendingList_;
137 :
138 : // Source compression worklist of tasks that can start.
139 : SourceCompressionTaskVector compressionWorklist_;
140 :
141 : // Finished source compression tasks.
142 : SourceCompressionTaskVector compressionFinishedList_;
143 :
144 : // Runtimes which have sweeping / allocating work to do.
145 : GCHelperStateVector gcHelperWorklist_;
146 :
147 : // GC tasks needing to be done in parallel.
148 : GCParallelTaskVector gcParallelWorklist_;
149 :
150 : ParseTask* removeFinishedParseTask(ParseTaskKind kind, JS::OffThreadToken* token);
151 :
152 : public:
153 :
154 : void addSizeOfIncludingThis(JS::GlobalStats* stats,
155 : AutoLockHelperThreadState& lock) const;
156 :
157 : size_t maxIonCompilationThreads() const;
158 : size_t maxWasmCompilationThreads() const;
159 : size_t maxWasmTier2GeneratorThreads() const;
160 : size_t maxPromiseHelperThreads() const;
161 : size_t maxParseThreads() const;
162 : size_t maxCompressionThreads() const;
163 : size_t maxGCHelperThreads() const;
164 : size_t maxGCParallelThreads() const;
165 :
166 : GlobalHelperThreadState();
167 :
168 : bool ensureInitialized();
169 : void finish();
170 : void finishThreads();
171 :
172 : void lock();
173 : void unlock();
174 : #ifdef DEBUG
175 : bool isLockedByCurrentThread() const;
176 : #endif
177 :
178 : enum CondVar {
179 : // For notifying threads waiting for work that they may be able to make
180 : // progress, ie, a work item has been completed by a helper thread and
181 : // the thread that created the work item can now consume it.
182 : CONSUMER,
183 :
184 : // For notifying helper threads doing the work that they may be able to
185 : // make progress, ie, a work item has been enqueued and an idle helper
186 : // thread may pick up up the work item and perform it.
187 : PRODUCER,
188 : };
189 :
190 : void wait(AutoLockHelperThreadState& locked, CondVar which,
191 : mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever());
192 : void notifyAll(CondVar which, const AutoLockHelperThreadState&);
193 : void notifyOne(CondVar which, const AutoLockHelperThreadState&);
194 :
195 : // Helper method for removing items from the vectors below while iterating over them.
196 : template <typename T>
197 0 : void remove(T& vector, size_t* index)
198 : {
199 : // Self-moving is undefined behavior.
200 0 : if (*index != vector.length() - 1)
201 0 : vector[*index] = std::move(vector.back());
202 49 : (*index)--;
203 49 : vector.popBack();
204 0 : }
205 :
206 : IonBuilderVector& ionWorklist(const AutoLockHelperThreadState&) {
207 0 : return ionWorklist_;
208 : }
209 : IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) {
210 0 : return ionFinishedList_;
211 : }
212 : IonBuilderVector& ionFreeList(const AutoLockHelperThreadState&) {
213 0 : return ionFreeList_;
214 : }
215 :
216 312 : wasm::CompileTaskPtrFifo& wasmWorklist(const AutoLockHelperThreadState&, wasm::CompileMode m) {
217 0 : switch (m) {
218 : case wasm::CompileMode::Once:
219 : case wasm::CompileMode::Tier1:
220 183 : return wasmWorklist_tier1_;
221 : case wasm::CompileMode::Tier2:
222 129 : return wasmWorklist_tier2_;
223 : default:
224 0 : MOZ_CRASH();
225 : }
226 : }
227 :
228 : wasm::Tier2GeneratorTaskPtrVector& wasmTier2GeneratorWorklist(const AutoLockHelperThreadState&) {
229 0 : return wasmTier2GeneratorWorklist_;
230 : }
231 :
232 : void incWasmTier2GeneratorsFinished(const AutoLockHelperThreadState&) {
233 0 : wasmTier2GeneratorsFinished_++;
234 : }
235 :
236 : uint32_t wasmTier2GeneratorsFinished(const AutoLockHelperThreadState&) const {
237 : return wasmTier2GeneratorsFinished_;
238 : }
239 :
240 : PromiseHelperTaskVector& promiseHelperTasks(const AutoLockHelperThreadState&) {
241 0 : return promiseHelperTasks_;
242 : }
243 :
244 : ParseTaskVector& parseWorklist(const AutoLockHelperThreadState&) {
245 0 : return parseWorklist_;
246 : }
247 : ParseTaskList& parseFinishedList(const AutoLockHelperThreadState&) {
248 0 : return parseFinishedList_;
249 : }
250 : ParseTaskVector& parseWaitingOnGC(const AutoLockHelperThreadState&) {
251 0 : return parseWaitingOnGC_;
252 : }
253 :
254 : SourceCompressionTaskVector& compressionPendingList(const AutoLockHelperThreadState&) {
255 416 : return compressionPendingList_;
256 : }
257 :
258 : SourceCompressionTaskVector& compressionWorklist(const AutoLockHelperThreadState&) {
259 0 : return compressionWorklist_;
260 : }
261 :
262 : SourceCompressionTaskVector& compressionFinishedList(const AutoLockHelperThreadState&) {
263 0 : return compressionFinishedList_;
264 : }
265 :
266 : GCHelperStateVector& gcHelperWorklist(const AutoLockHelperThreadState&) {
267 0 : return gcHelperWorklist_;
268 : }
269 :
270 : GCParallelTaskVector& gcParallelWorklist(const AutoLockHelperThreadState&) {
271 42 : return gcParallelWorklist_;
272 : }
273 :
274 : bool canStartWasmCompile(const AutoLockHelperThreadState& lock, wasm::CompileMode mode);
275 :
276 : bool canStartWasmTier1Compile(const AutoLockHelperThreadState& lock);
277 : bool canStartWasmTier2Compile(const AutoLockHelperThreadState& lock);
278 : bool canStartWasmTier2Generator(const AutoLockHelperThreadState& lock);
279 : bool canStartPromiseHelperTask(const AutoLockHelperThreadState& lock);
280 : bool canStartIonCompile(const AutoLockHelperThreadState& lock);
281 : bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
282 : bool canStartParseTask(const AutoLockHelperThreadState& lock);
283 : bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
284 : bool canStartGCHelperTask(const AutoLockHelperThreadState& lock);
285 : bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
286 :
287 : // Used by a major GC to signal processing enqueued compression tasks.
288 : void startHandlingCompressionTasks(const AutoLockHelperThreadState&);
289 :
290 : private:
291 : void scheduleCompressionTasks(const AutoLockHelperThreadState&);
292 :
293 : public:
294 : jit::IonBuilder* highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock);
295 :
296 : template <
297 : typename F,
298 : typename = typename mozilla::EnableIf<
299 : // Matches when the type is a function or lambda with the signature `bool(ParseTask*)`
300 : mozilla::IsSame<bool, decltype((*(F*)nullptr)((ParseTask*)nullptr))>::value
301 : >::Type
302 : >
303 : bool finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token, F&& finishCallback);
304 :
305 : JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token);
306 :
307 : bool finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token, MutableHandle<ScriptVector> scripts);
308 :
309 : void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, JS::OffThreadToken* token);
310 : void destroyParseTask(JSRuntime* rt, ParseTask* parseTask);
311 :
312 : void mergeParseTaskRealm(JSContext* cx, ParseTask* parseTask, JS::Realm* dest);
313 :
314 : void trace(JSTracer* trc, js::gc::AutoTraceSession& session);
315 :
316 : JSScript* finishScriptParseTask(JSContext* cx, JS::OffThreadToken* token);
317 : JSScript* finishScriptDecodeTask(JSContext* cx, JS::OffThreadToken* token);
318 : bool finishMultiScriptsDecodeTask(JSContext* cx, JS::OffThreadToken* token, MutableHandle<ScriptVector> scripts);
319 : JSObject* finishModuleParseTask(JSContext* cx, JS::OffThreadToken* token);
320 :
321 : #if defined(JS_BUILD_BINAST)
322 : JSScript* finishBinASTDecodeTask(JSContext* cx, JS::OffThreadToken* token);
323 : #endif
324 :
325 : bool hasActiveThreads(const AutoLockHelperThreadState&);
326 : void waitForAllThreads();
327 : void waitForAllThreadsLocked(AutoLockHelperThreadState&);
328 :
329 : template <typename T>
330 : bool checkTaskThreadLimit(size_t maxThreads, bool isMaster = false) const;
331 :
332 : private:
333 : /*
334 : * Lock protecting all mutable shared state accessed by helper threads, and
335 : * used by all condition variables.
336 : */
337 : js::Mutex helperLock;
338 :
339 : /* Condvars for threads waiting/notifying each other. */
340 : js::ConditionVariable consumerWakeup;
341 : js::ConditionVariable producerWakeup;
342 :
343 0 : js::ConditionVariable& whichWakeup(CondVar which) {
344 1 : switch (which) {
345 78 : case CONSUMER: return consumerWakeup;
346 254 : case PRODUCER: return producerWakeup;
347 0 : default: MOZ_CRASH("Invalid CondVar in |whichWakeup|");
348 : }
349 : }
350 : };
351 :
352 : static inline GlobalHelperThreadState&
353 7851 : HelperThreadState()
354 : {
355 : extern GlobalHelperThreadState* gHelperThreadState;
356 :
357 7851 : MOZ_ASSERT(gHelperThreadState);
358 7851 : return *gHelperThreadState;
359 : }
360 :
361 : typedef mozilla::Variant<jit::IonBuilder*,
362 : wasm::CompileTask*,
363 : wasm::Tier2GeneratorTask*,
364 : PromiseHelperTask*,
365 : ParseTask*,
366 : SourceCompressionTask*,
367 : GCHelperState*,
368 : GCParallelTask*> HelperTaskUnion;
369 :
370 : /* Individual helper thread, one allocated per core. */
371 12 : struct HelperThread
372 : {
373 : mozilla::Maybe<Thread> thread;
374 :
375 : /*
376 : * Indicate to a thread that it should terminate itself. This is only read
377 : * or written with the helper thread state lock held.
378 : */
379 : bool terminate;
380 :
381 : /*
382 : * Indicate that this thread has been registered and needs to be
383 : * unregistered at shutdown.
384 : */
385 : bool registered;
386 :
387 : /* The current task being executed by this thread, if any. */
388 : mozilla::Maybe<HelperTaskUnion> currentTask;
389 :
390 : bool idle() const {
391 379 : return currentTask.isNothing();
392 : }
393 :
394 : /* Any builder currently being compiled by Ion on this thread. */
395 : jit::IonBuilder* ionBuilder() {
396 0 : return maybeCurrentTaskAs<jit::IonBuilder*>();
397 : }
398 :
399 : /* Any wasm data currently being optimized on this thread. */
400 : wasm::CompileTask* wasmTask() {
401 0 : return maybeCurrentTaskAs<wasm::CompileTask*>();
402 : }
403 :
404 : wasm::Tier2GeneratorTask* wasmTier2GeneratorTask() {
405 0 : return maybeCurrentTaskAs<wasm::Tier2GeneratorTask*>();
406 : }
407 :
408 : /* Any source being parsed/emitted on this thread. */
409 : ParseTask* parseTask() {
410 1451 : return maybeCurrentTaskAs<ParseTask*>();
411 : }
412 :
413 : /* Any source being compressed on this thread. */
414 : SourceCompressionTask* compressionTask() {
415 0 : return maybeCurrentTaskAs<SourceCompressionTask*>();
416 : }
417 :
418 : /* Any GC state for background sweeping or allocating being performed. */
419 : GCHelperState* gcHelperTask() {
420 0 : return maybeCurrentTaskAs<GCHelperState*>();
421 : }
422 :
423 : /* State required to perform a GC parallel task. */
424 : GCParallelTask* gcParallelTask() {
425 21 : return maybeCurrentTaskAs<GCParallelTask*>();
426 : }
427 :
428 : void destroy();
429 :
430 : static void ThreadMain(void* arg);
431 : void threadLoop();
432 :
433 : void ensureRegisteredWithProfiler();
434 : void unregisterWithProfilerIfNeeded();
435 :
436 : private:
437 : struct TaskSpec
438 : {
439 : using Selector = bool(GlobalHelperThreadState::*)(const AutoLockHelperThreadState&);
440 : using Handler = void(HelperThread::*)(AutoLockHelperThreadState&);
441 :
442 : js::ThreadType type;
443 : Selector canStart;
444 : Handler handleWorkload;
445 : };
446 :
447 : static const TaskSpec taskSpecs[];
448 :
449 : const TaskSpec* findHighestPriorityTask(const AutoLockHelperThreadState& locked);
450 :
451 : template <typename T>
452 3162 : T maybeCurrentTaskAs() {
453 3162 : if (currentTask.isSome() && currentTask->is<T>())
454 1738 : return currentTask->as<T>();
455 :
456 : return nullptr;
457 : }
458 :
459 : void handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode);
460 :
461 : void handleWasmTier1Workload(AutoLockHelperThreadState& locked);
462 : void handleWasmTier2Workload(AutoLockHelperThreadState& locked);
463 : void handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked);
464 : void handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked);
465 : void handleIonWorkload(AutoLockHelperThreadState& locked);
466 : void handleIonFreeWorkload(AutoLockHelperThreadState& locked);
467 : void handleParseWorkload(AutoLockHelperThreadState& locked);
468 : void handleCompressionWorkload(AutoLockHelperThreadState& locked);
469 : void handleGCHelperWorkload(AutoLockHelperThreadState& locked);
470 : void handleGCParallelWorkload(AutoLockHelperThreadState& locked);
471 : };
472 :
473 : /* Methods for interacting with helper threads. */
474 :
475 : // Create data structures used by helper threads.
476 : bool
477 : CreateHelperThreadsState();
478 :
479 : // Destroy data structures used by helper threads.
480 : void
481 : DestroyHelperThreadsState();
482 :
483 : // Initialize helper threads unless already initialized.
484 : bool
485 : EnsureHelperThreadsInitialized();
486 :
487 : // This allows the JS shell to override GetCPUCount() when passed the
488 : // --thread-count=N option.
489 : void
490 : SetFakeCPUCount(size_t count);
491 :
492 : // Get the current helper thread, or null.
493 : HelperThread*
494 : CurrentHelperThread();
495 :
496 : // Enqueues a wasm compilation task.
497 : bool
498 : StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode);
499 :
500 : namespace wasm {
501 :
502 : // Called on a helper thread after StartOffThreadWasmCompile.
503 : void
504 : ExecuteCompileTaskFromHelperThread(CompileTask* task);
505 :
506 : }
507 :
508 : // Enqueues a wasm compilation task.
509 : void
510 : StartOffThreadWasmTier2Generator(wasm::UniqueTier2GeneratorTask task);
511 :
512 : // Cancel all background Wasm Tier-2 compilations.
513 : void
514 : CancelOffThreadWasmTier2Generator();
515 :
516 : /*
517 : * If helper threads are available, call execute() then dispatchResolve() on the
518 : * given task in a helper thread. If no helper threads are available, the given
519 : * task is executed and resolved synchronously.
520 : */
521 : bool
522 : StartOffThreadPromiseHelperTask(JSContext* cx, UniquePtr<PromiseHelperTask> task);
523 :
524 : bool
525 : StartOffThreadPromiseHelperTask(PromiseHelperTask* task);
526 :
527 : /*
528 : * Schedule an Ion compilation for a script, given a builder which has been
529 : * generated and read everything needed from the VM state.
530 : */
531 : bool
532 : StartOffThreadIonCompile(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock);
533 :
534 : /*
535 : * Schedule deletion of Ion compilation data.
536 : */
537 : bool
538 : StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock);
539 :
540 : struct AllCompilations {};
541 : struct ZonesInState { JSRuntime* runtime; JS::Zone::GCState state; };
542 : struct CompilationsUsingNursery { JSRuntime* runtime; };
543 :
544 : using CompilationSelector = mozilla::Variant<JSScript*,
545 : JS::Realm*,
546 : Zone*,
547 : ZonesInState,
548 : JSRuntime*,
549 : CompilationsUsingNursery,
550 : AllCompilations>;
551 :
552 : /*
553 : * Cancel scheduled or in progress Ion compilations.
554 : */
555 : void
556 : CancelOffThreadIonCompile(const CompilationSelector& selector, bool discardLazyLinkList);
557 :
558 : inline void
559 1 : CancelOffThreadIonCompile(JSScript* script)
560 : {
561 1 : CancelOffThreadIonCompile(CompilationSelector(script), true);
562 1 : }
563 :
564 : inline void
565 0 : CancelOffThreadIonCompile(JS::Realm* realm)
566 : {
567 0 : CancelOffThreadIonCompile(CompilationSelector(realm), true);
568 0 : }
569 :
570 : inline void
571 0 : CancelOffThreadIonCompile(Zone* zone)
572 : {
573 0 : CancelOffThreadIonCompile(CompilationSelector(zone), true);
574 0 : }
575 :
576 : inline void
577 0 : CancelOffThreadIonCompile(JSRuntime* runtime, JS::Zone::GCState state)
578 : {
579 0 : CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state}), true);
580 0 : }
581 :
582 : inline void
583 0 : CancelOffThreadIonCompile(JSRuntime* runtime)
584 : {
585 0 : CancelOffThreadIonCompile(CompilationSelector(runtime), true);
586 0 : }
587 :
588 : inline void
589 3 : CancelOffThreadIonCompilesUsingNurseryPointers(JSRuntime* runtime)
590 : {
591 9 : CancelOffThreadIonCompile(CompilationSelector(CompilationsUsingNursery{runtime}), true);
592 3 : }
593 :
594 : #ifdef DEBUG
595 : bool
596 : HasOffThreadIonCompile(JS::Realm* realm);
597 : #endif
598 :
599 : /* Cancel all scheduled, in progress or finished parses for runtime. */
600 : void
601 : CancelOffThreadParses(JSRuntime* runtime);
602 :
603 : /*
604 : * Start a parse/emit cycle for a stream of source. The characters must stay
605 : * alive until the compilation finishes.
606 : */
607 : bool
608 : StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
609 : const char16_t* chars, size_t length,
610 : JS::OffThreadCompileCallback callback, void* callbackData);
611 :
612 : bool
613 : StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
614 : const char16_t* chars, size_t length,
615 : JS::OffThreadCompileCallback callback, void* callbackData);
616 :
617 : bool
618 : StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
619 : const JS::TranscodeRange& range,
620 : JS::OffThreadCompileCallback callback, void* callbackData);
621 :
622 : #if defined(JS_BUILD_BINAST)
623 :
624 : bool
625 : StartOffThreadDecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
626 : const uint8_t* buf, size_t length,
627 : JS::OffThreadCompileCallback callback, void* callbackData);
628 :
629 : #endif /* JS_BUILD_BINAST */
630 :
631 : bool
632 : StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
633 : JS::TranscodeSources& sources,
634 : JS::OffThreadCompileCallback callback, void* callbackData);
635 :
636 : /*
637 : * Called at the end of GC to enqueue any Parse tasks that were waiting on an
638 : * atoms-zone GC to finish.
639 : */
640 : void
641 : EnqueuePendingParseTasksAfterGC(JSRuntime* rt);
642 :
643 : struct AutoEnqueuePendingParseTasksAfterGC {
644 : const gc::GCRuntime& gc_;
645 0 : explicit AutoEnqueuePendingParseTasksAfterGC(const gc::GCRuntime& gc) : gc_(gc) {}
646 : ~AutoEnqueuePendingParseTasksAfterGC();
647 : };
648 :
649 : // Enqueue a compression job to be processed if there's a major GC.
650 : bool
651 : EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task);
652 :
653 : // Cancel all scheduled, in progress, or finished compression tasks for
654 : // runtime.
655 : void
656 : CancelOffThreadCompressions(JSRuntime* runtime);
657 :
658 2050 : class MOZ_RAII AutoLockHelperThreadState : public LockGuard<Mutex>
659 : {
660 : using Base = LockGuard<Mutex>;
661 :
662 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
663 :
664 : public:
665 : explicit AutoLockHelperThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
666 3087 : : Base(HelperThreadState().helperLock)
667 : {
668 2058 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
669 : }
670 : };
671 :
672 0 : class MOZ_RAII AutoUnlockHelperThreadState : public UnlockGuard<Mutex>
673 : {
674 : using Base = UnlockGuard<Mutex>;
675 :
676 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
677 :
678 : public:
679 :
680 : explicit AutoUnlockHelperThreadState(AutoLockHelperThreadState& locked
681 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
682 375 : : Base(locked)
683 : {
684 250 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
685 : }
686 : };
687 :
688 : struct ParseTask : public mozilla::LinkedListElement<ParseTask>, public JS::OffThreadToken
689 : {
690 : ParseTaskKind kind;
691 : OwningCompileOptions options;
692 :
693 : LifoAlloc alloc;
694 :
695 : // The global object to use while parsing.
696 : JSObject* parseGlobal;
697 :
698 : // Callback invoked off thread when the parse finishes.
699 : JS::OffThreadCompileCallback callback;
700 : void* callbackData;
701 :
702 : // Holds the final scripts between the invocation of the callback and the
703 : // point where FinishOffThreadScript is called, which will destroy the
704 : // ParseTask.
705 : GCVector<JSScript*, 1> scripts;
706 :
707 : // Holds the ScriptSourceObjects generated for the script compilation.
708 : GCVector<ScriptSourceObject*, 1> sourceObjects;
709 :
710 : // Any errors or warnings produced during compilation. These are reported
711 : // when finishing the script.
712 : Vector<CompileError*, 0, SystemAllocPolicy> errors;
713 : bool overRecursed;
714 : bool outOfMemory;
715 :
716 : ParseTask(ParseTaskKind kind, JSContext* cx,
717 : JS::OffThreadCompileCallback callback, void* callbackData);
718 : virtual ~ParseTask();
719 :
720 : bool init(JSContext* cx, const ReadOnlyCompileOptions& options, JSObject* global);
721 :
722 : void activate(JSRuntime* rt);
723 : virtual void parse(JSContext* cx) = 0;
724 : bool finish(JSContext* cx);
725 :
726 : bool runtimeMatches(JSRuntime* rt) {
727 0 : return parseGlobal->runtimeFromAnyThread() == rt;
728 : }
729 :
730 : void trace(JSTracer* trc);
731 :
732 : size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
733 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
734 0 : return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
735 : }
736 : };
737 :
738 5 : struct ScriptParseTask : public ParseTask
739 : {
740 : JS::TwoByteChars data;
741 :
742 : ScriptParseTask(JSContext* cx, const char16_t* chars, size_t length,
743 : JS::OffThreadCompileCallback callback, void* callbackData);
744 : void parse(JSContext* cx) override;
745 : };
746 :
747 0 : struct ModuleParseTask : public ParseTask
748 : {
749 : JS::TwoByteChars data;
750 :
751 : ModuleParseTask(JSContext* cx, const char16_t* chars, size_t length,
752 : JS::OffThreadCompileCallback callback, void* callbackData);
753 : void parse(JSContext* cx) override;
754 : };
755 :
756 0 : struct ScriptDecodeTask : public ParseTask
757 : {
758 : const JS::TranscodeRange range;
759 :
760 : ScriptDecodeTask(JSContext* cx, const JS::TranscodeRange& range,
761 : JS::OffThreadCompileCallback callback, void* callbackData);
762 : void parse(JSContext* cx) override;
763 : };
764 :
765 : #if defined(JS_BUILD_BINAST)
766 :
767 0 : struct BinASTDecodeTask : public ParseTask
768 : {
769 : mozilla::Range<const uint8_t> data;
770 :
771 : BinASTDecodeTask(JSContext* cx, const uint8_t* buf, size_t length,
772 : JS::OffThreadCompileCallback callback, void* callbackData);
773 : void parse(JSContext* cx) override;
774 : };
775 :
776 : #endif /* JS_BUILD_BINAST */
777 :
778 0 : struct MultiScriptsDecodeTask : public ParseTask
779 : {
780 : JS::TranscodeSources* sources;
781 :
782 : MultiScriptsDecodeTask(JSContext* cx, JS::TranscodeSources& sources,
783 : JS::OffThreadCompileCallback callback, void* callbackData);
784 : void parse(JSContext* cx) override;
785 : };
786 :
787 : // Return whether, if a new parse task was started, it would need to wait for
788 : // an in-progress GC to complete before starting.
789 : extern bool
790 : OffThreadParsingMustWaitForGC(JSRuntime* rt);
791 :
792 : // It is not desirable to eagerly compress: if lazy functions that are tied to
793 : // the ScriptSource were to be executed relatively soon after parsing, they
794 : // would need to block on decompression, which hurts responsiveness.
795 : //
796 : // To this end, compression tasks are heap allocated and enqueued in a pending
797 : // list by ScriptSource::setSourceCopy. When a major GC occurs, we schedule
798 : // pending compression tasks and move the ones that are ready to be compressed
799 : // to the worklist. Currently, a compression task is considered ready 2 major
800 : // GCs after being enqueued. Completed tasks are handled during the sweeping
801 : // phase by AttachCompressedSourcesTask, which runs in parallel with other GC
802 : // sweeping tasks.
803 0 : class SourceCompressionTask
804 : {
805 : friend struct HelperThread;
806 : friend class ScriptSource;
807 :
808 : // The runtime that the ScriptSource is associated with, in the sense that
809 : // it uses the runtime's immutable string cache.
810 : JSRuntime* runtime_;
811 :
812 : // The major GC number of the runtime when the task was enqueued.
813 : uint64_t majorGCNumber_;
814 :
815 : // The source to be compressed.
816 : ScriptSourceHolder sourceHolder_;
817 :
818 : // The resultant compressed string. If the compressed string is larger
819 : // than the original, or we OOM'd during compression, or nothing else
820 : // except the task is holding the ScriptSource alive when scheduled to
821 : // compress, this will remain None upon completion.
822 : mozilla::Maybe<SharedImmutableString> resultString_;
823 :
824 : public:
825 : // The majorGCNumber is used for scheduling tasks.
826 0 : SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
827 416 : : runtime_(rt),
828 832 : majorGCNumber_(rt->gc.majorGCCount()),
829 1664 : sourceHolder_(source)
830 416 : { }
831 :
832 : bool runtimeMatches(JSRuntime* runtime) const {
833 : return runtime == runtime_;
834 : }
835 : bool shouldStart() const {
836 : // We wait 2 major GCs to start compressing, in order to avoid
837 : // immediate compression.
838 0 : return runtime_->gc.majorGCCount() > majorGCNumber_ + 1;
839 : }
840 :
841 : bool shouldCancel() const {
842 : // If the refcount is exactly 1, then nothing else is holding on to the
843 : // ScriptSource, so no reason to compress it and we should cancel the task.
844 0 : return sourceHolder_.get()->refs == 1;
845 : }
846 :
847 : void work();
848 : void complete();
849 : };
850 :
851 : // A PromiseHelperTask is an OffThreadPromiseTask that executes a single job on
852 : // a helper thread. Derived classes do their helper-thread work by implementing
853 : // execute().
854 : struct PromiseHelperTask : OffThreadPromiseTask
855 : {
856 : PromiseHelperTask(JSContext* cx, Handle<PromiseObject*> promise)
857 : : OffThreadPromiseTask(cx, promise)
858 : {}
859 :
860 : // To be called on a helper thread and implemented by the derived class.
861 : virtual void execute() = 0;
862 :
863 : // May be called in the absence of helper threads or off-thread promise
864 : // support to synchronously execute and resolve a PromiseTask.
865 : //
866 : // Warning: After this function returns, 'this' can be deleted at any time, so the
867 : // caller must immediately return from the stream callback.
868 : void executeAndResolveAndDestroy(JSContext* cx);
869 : };
870 :
871 : } /* namespace js */
872 :
873 : #endif /* vm_HelperThreads_h */
|