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 : #include "jscntxt.h"
25 :
26 : #include "jit/Ion.h"
27 : #include "threading/ConditionVariable.h"
28 : #include "vm/MutexIDs.h"
29 :
30 : namespace JS {
31 : struct Zone;
32 : } // namespace JS
33 :
34 : namespace js {
35 :
36 : class AutoLockHelperThreadState;
37 : class AutoUnlockHelperThreadState;
38 : class CompileError;
39 : class PromiseTask;
40 : struct HelperThread;
41 : struct ParseTask;
42 : namespace jit {
43 : class IonBuilder;
44 : } // namespace jit
45 : namespace wasm {
46 : class FuncIR;
47 : class FunctionCompileResults;
48 : class CompileTask;
49 : typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
50 : } // namespace wasm
51 :
52 : enum class ParseTaskKind
53 : {
54 : Script,
55 : Module,
56 : ScriptDecode,
57 : MultiScriptsDecode
58 : };
59 :
60 : // Per-process state for off thread work items.
61 0 : class GlobalHelperThreadState
62 : {
63 : friend class AutoLockHelperThreadState;
64 : friend class AutoUnlockHelperThreadState;
65 :
66 : public:
67 : // Number of CPUs to treat this machine as having when creating threads.
68 : // May be accessed without locking.
69 : size_t cpuCount;
70 :
71 : // Number of threads to create. May be accessed without locking.
72 : size_t threadCount;
73 :
74 : typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
75 : typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
76 : typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector;
77 : typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
78 : typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
79 : typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskVector;
80 :
81 : // List of available threads, or null if the thread state has not been initialized.
82 : using HelperThreadVector = Vector<HelperThread, 0, SystemAllocPolicy>;
83 : UniquePtr<HelperThreadVector> threads;
84 :
85 : private:
86 : // The lists below are all protected by |lock|.
87 :
88 : // Ion compilation worklist and finished jobs.
89 : IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_;
90 :
91 : // wasm worklist and finished jobs.
92 : wasm::CompileTaskPtrVector wasmWorklist_, wasmFinishedList_;
93 :
94 : public:
95 : // For now, only allow a single parallel wasm compilation to happen at a
96 : // time. This avoids race conditions on wasmWorklist/wasmFinishedList/etc.
97 : mozilla::Atomic<bool> wasmCompilationInProgress;
98 :
99 : private:
100 : // Async tasks that, upon completion, are dispatched back to the JSContext's
101 : // owner thread via embedding callbacks instead of a finished list.
102 : PromiseTaskVector promiseTasks_;
103 :
104 : // Script parsing/emitting worklist and finished jobs.
105 : ParseTaskVector parseWorklist_, parseFinishedList_;
106 :
107 : // Parse tasks waiting for an atoms-zone GC to complete.
108 : ParseTaskVector parseWaitingOnGC_;
109 :
110 : // Source compression worklist of tasks that we do not yet know can start.
111 : SourceCompressionTaskVector compressionPendingList_;
112 :
113 : // Source compression worklist of tasks that can start.
114 : SourceCompressionTaskVector compressionWorklist_;
115 :
116 : // Finished source compression tasks.
117 : SourceCompressionTaskVector compressionFinishedList_;
118 :
119 : // Runtimes which have sweeping / allocating work to do.
120 : GCHelperStateVector gcHelperWorklist_;
121 :
122 : // GC tasks needing to be done in parallel.
123 : GCParallelTaskVector gcParallelWorklist_;
124 :
125 : ParseTask* removeFinishedParseTask(ParseTaskKind kind, void* token);
126 :
127 : public:
128 : size_t maxIonCompilationThreads() const;
129 : size_t maxUnpausedIonCompilationThreads() const;
130 : size_t maxWasmCompilationThreads() const;
131 : size_t maxParseThreads() const;
132 : size_t maxCompressionThreads() const;
133 : size_t maxGCHelperThreads() const;
134 : size_t maxGCParallelThreads() const;
135 :
136 : GlobalHelperThreadState();
137 :
138 : bool ensureInitialized();
139 : void finish();
140 : void finishThreads();
141 :
142 : void lock();
143 : void unlock();
144 : #ifdef DEBUG
145 : bool isLockedByCurrentThread();
146 : #endif
147 :
148 : enum CondVar {
149 : // For notifying threads waiting for work that they may be able to make progress.
150 : CONSUMER,
151 :
152 : // For notifying threads doing work that they may be able to make progress.
153 : PRODUCER,
154 :
155 : // For notifying threads doing work which are paused that they may be
156 : // able to resume making progress.
157 : PAUSE
158 : };
159 :
160 : void wait(AutoLockHelperThreadState& locked, CondVar which,
161 : mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever());
162 : void notifyAll(CondVar which, const AutoLockHelperThreadState&);
163 : void notifyOne(CondVar which, const AutoLockHelperThreadState&);
164 :
165 : // Helper method for removing items from the vectors below while iterating over them.
166 : template <typename T>
167 23 : void remove(T& vector, size_t* index)
168 : {
169 : // Self-moving is undefined behavior.
170 23 : if (*index != vector.length() - 1)
171 1 : vector[*index] = mozilla::Move(vector.back());
172 23 : (*index)--;
173 23 : vector.popBack();
174 23 : }
175 :
176 413 : IonBuilderVector& ionWorklist(const AutoLockHelperThreadState&) {
177 413 : return ionWorklist_;
178 : }
179 258 : IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) {
180 258 : return ionFinishedList_;
181 : }
182 130 : IonBuilderVector& ionFreeList(const AutoLockHelperThreadState&) {
183 130 : return ionFreeList_;
184 : }
185 :
186 161 : wasm::CompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&) {
187 161 : return wasmWorklist_;
188 : }
189 0 : wasm::CompileTaskPtrVector& wasmFinishedList(const AutoLockHelperThreadState&) {
190 0 : return wasmFinishedList_;
191 : }
192 :
193 161 : PromiseTaskVector& promiseTasks(const AutoLockHelperThreadState&) {
194 161 : return promiseTasks_;
195 : }
196 :
197 209 : ParseTaskVector& parseWorklist(const AutoLockHelperThreadState&) {
198 209 : return parseWorklist_;
199 : }
200 30 : ParseTaskVector& parseFinishedList(const AutoLockHelperThreadState&) {
201 30 : return parseFinishedList_;
202 : }
203 0 : ParseTaskVector& parseWaitingOnGC(const AutoLockHelperThreadState&) {
204 0 : return parseWaitingOnGC_;
205 : }
206 :
207 156 : SourceCompressionTaskVector& compressionPendingList(const AutoLockHelperThreadState&) {
208 156 : return compressionPendingList_;
209 : }
210 :
211 131 : SourceCompressionTaskVector& compressionWorklist(const AutoLockHelperThreadState&) {
212 131 : return compressionWorklist_;
213 : }
214 :
215 0 : SourceCompressionTaskVector& compressionFinishedList(const AutoLockHelperThreadState&) {
216 0 : return compressionFinishedList_;
217 : }
218 :
219 153 : GCHelperStateVector& gcHelperWorklist(const AutoLockHelperThreadState&) {
220 153 : return gcHelperWorklist_;
221 : }
222 :
223 245 : GCParallelTaskVector& gcParallelWorklist(const AutoLockHelperThreadState&) {
224 245 : return gcParallelWorklist_;
225 : }
226 :
227 : bool canStartWasmCompile(const AutoLockHelperThreadState& lock);
228 : bool canStartPromiseTask(const AutoLockHelperThreadState& lock);
229 : bool canStartIonCompile(const AutoLockHelperThreadState& lock);
230 : bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
231 : bool canStartParseTask(const AutoLockHelperThreadState& lock);
232 : bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
233 : bool canStartGCHelperTask(const AutoLockHelperThreadState& lock);
234 : bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
235 :
236 : // Used by a major GC to signal processing enqueued compression tasks.
237 : void startHandlingCompressionTasks(const AutoLockHelperThreadState&);
238 : void scheduleCompressionTasks(const AutoLockHelperThreadState&);
239 :
240 : // Unlike the methods above, the value returned by this method can change
241 : // over time, even if the helper thread state lock is held throughout.
242 : bool pendingIonCompileHasSufficientPriority(const AutoLockHelperThreadState& lock);
243 :
244 : jit::IonBuilder* highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock,
245 : bool remove = false);
246 : HelperThread* lowestPriorityUnpausedIonCompileAtThreshold(
247 : const AutoLockHelperThreadState& lock);
248 : HelperThread* highestPriorityPausedIonCompile(const AutoLockHelperThreadState& lock);
249 :
250 0 : uint32_t harvestFailedWasmJobs(const AutoLockHelperThreadState&) {
251 0 : uint32_t n = numWasmFailedJobs;
252 0 : numWasmFailedJobs = 0;
253 0 : return n;
254 : }
255 0 : UniqueChars harvestWasmError(const AutoLockHelperThreadState&) {
256 0 : return Move(firstWasmError);
257 : }
258 0 : void noteWasmFailure(const AutoLockHelperThreadState&) {
259 : // Be mindful to signal the active thread after calling this function.
260 0 : numWasmFailedJobs++;
261 0 : }
262 0 : void setWasmError(const AutoLockHelperThreadState&, UniqueChars error) {
263 0 : if (!firstWasmError)
264 0 : firstWasmError = Move(error);
265 0 : }
266 0 : bool wasmFailed(const AutoLockHelperThreadState&) {
267 0 : return bool(numWasmFailedJobs);
268 : }
269 :
270 : template <
271 : typename F,
272 : typename = typename mozilla::EnableIf<
273 : // Matches when the type is a function or lambda with the signature `bool(ParseTask*)`
274 : mozilla::IsSame<bool, decltype((*(F*)nullptr)((ParseTask*)nullptr))>::value
275 : >::Type
276 : >
277 : bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback);
278 :
279 : JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
280 :
281 : bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, MutableHandle<ScriptVector> scripts);
282 :
283 : void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token);
284 :
285 : void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
286 : Handle<GlobalObject*> global,
287 : JSCompartment* dest);
288 :
289 : void trace(JSTracer* trc);
290 :
291 : private:
292 : /*
293 : * Number of wasm jobs that encountered failure for the active module.
294 : * Their parent is logically the active thread, and this number serves for harvesting.
295 : */
296 : uint32_t numWasmFailedJobs;
297 : /*
298 : * Error string from wasm validation. Arbitrarily choose to keep the first one that gets
299 : * reported. Nondeterministic if multiple threads have errors.
300 : */
301 : UniqueChars firstWasmError;
302 :
303 : public:
304 : JSScript* finishScriptParseTask(JSContext* cx, void* token);
305 : JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
306 : bool finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts);
307 : JSObject* finishModuleParseTask(JSContext* cx, void* token);
308 :
309 : bool hasActiveThreads(const AutoLockHelperThreadState&);
310 : void waitForAllThreads();
311 :
312 : template <typename T>
313 : bool checkTaskThreadLimit(size_t maxThreads) const;
314 :
315 : private:
316 :
317 : /*
318 : * Lock protecting all mutable shared state accessed by helper threads, and
319 : * used by all condition variables.
320 : */
321 : js::Mutex helperLock;
322 :
323 : /* Condvars for threads waiting/notifying each other. */
324 : js::ConditionVariable consumerWakeup;
325 : js::ConditionVariable producerWakeup;
326 : js::ConditionVariable pauseWakeup;
327 :
328 216 : js::ConditionVariable& whichWakeup(CondVar which) {
329 216 : switch (which) {
330 69 : case CONSUMER: return consumerWakeup;
331 145 : case PRODUCER: return producerWakeup;
332 2 : case PAUSE: return pauseWakeup;
333 0 : default: MOZ_CRASH("Invalid CondVar in |whichWakeup|");
334 : }
335 : }
336 : };
337 :
338 : static inline GlobalHelperThreadState&
339 4631 : HelperThreadState()
340 : {
341 : extern GlobalHelperThreadState* gHelperThreadState;
342 :
343 4631 : MOZ_ASSERT(gHelperThreadState);
344 4631 : return *gHelperThreadState;
345 : }
346 :
347 : typedef mozilla::Variant<jit::IonBuilder*,
348 : wasm::CompileTask*,
349 : PromiseTask*,
350 : ParseTask*,
351 : SourceCompressionTask*,
352 : GCHelperState*,
353 : GCParallelTask*> HelperTaskUnion;
354 :
355 : /* Individual helper thread, one allocated per core. */
356 36 : struct HelperThread
357 : {
358 : mozilla::Maybe<Thread> thread;
359 :
360 : /*
361 : * Indicate to a thread that it should terminate itself. This is only read
362 : * or written with the helper thread state lock held.
363 : */
364 : bool terminate;
365 :
366 : /*
367 : * Indicate to a thread that it should pause execution. This is only
368 : * written with the helper thread state lock held, but may be read from
369 : * without the lock held.
370 : */
371 : mozilla::Atomic<bool, mozilla::Relaxed> pause;
372 :
373 : /* The current task being executed by this thread, if any. */
374 : mozilla::Maybe<HelperTaskUnion> currentTask;
375 :
376 145 : bool idle() const {
377 145 : return currentTask.isNothing();
378 : }
379 :
380 : /* Any builder currently being compiled by Ion on this thread. */
381 3100 : jit::IonBuilder* ionBuilder() {
382 3100 : return maybeCurrentTaskAs<jit::IonBuilder*>();
383 : }
384 :
385 : /* Any wasm data currently being optimized on this thread. */
386 0 : wasm::CompileTask* wasmTask() {
387 0 : return maybeCurrentTaskAs<wasm::CompileTask*>();
388 : }
389 :
390 : /* Any source being parsed/emitted on this thread. */
391 16 : ParseTask* parseTask() {
392 16 : return maybeCurrentTaskAs<ParseTask*>();
393 : }
394 :
395 : /* Any source being compressed on this thread. */
396 0 : SourceCompressionTask* compressionTask() {
397 0 : return maybeCurrentTaskAs<SourceCompressionTask*>();
398 : }
399 :
400 : /* Any GC state for background sweeping or allocating being performed. */
401 0 : GCHelperState* gcHelperTask() {
402 0 : return maybeCurrentTaskAs<GCHelperState*>();
403 : }
404 :
405 : /* State required to perform a GC parallel task. */
406 23 : GCParallelTask* gcParallelTask() {
407 23 : return maybeCurrentTaskAs<GCParallelTask*>();
408 : }
409 :
410 : void destroy();
411 :
412 : static void ThreadMain(void* arg);
413 : void threadLoop();
414 :
415 : private:
416 : template <typename T>
417 3139 : T maybeCurrentTaskAs() {
418 3139 : if (currentTask.isSome() && currentTask->is<T>())
419 45 : return currentTask->as<T>();
420 :
421 3094 : return nullptr;
422 : }
423 :
424 : void handleWasmWorkload(AutoLockHelperThreadState& locked);
425 : void handlePromiseTaskWorkload(AutoLockHelperThreadState& locked);
426 : void handleIonWorkload(AutoLockHelperThreadState& locked);
427 : void handleIonFreeWorkload(AutoLockHelperThreadState& locked);
428 : void handleParseWorkload(AutoLockHelperThreadState& locked);
429 : void handleCompressionWorkload(AutoLockHelperThreadState& locked);
430 : void handleGCHelperWorkload(AutoLockHelperThreadState& locked);
431 : void handleGCParallelWorkload(AutoLockHelperThreadState& locked);
432 : };
433 :
434 : /* Methods for interacting with helper threads. */
435 :
436 : // Create data structures used by helper threads.
437 : bool
438 : CreateHelperThreadsState();
439 :
440 : // Destroy data structures used by helper threads.
441 : void
442 : DestroyHelperThreadsState();
443 :
444 : // Initialize helper threads unless already initialized.
445 : bool
446 : EnsureHelperThreadsInitialized();
447 :
448 : // This allows the JS shell to override GetCPUCount() when passed the
449 : // --thread-count=N option.
450 : void
451 : SetFakeCPUCount(size_t count);
452 :
453 : // Get the current helper thread, or null.
454 : HelperThread*
455 : CurrentHelperThread();
456 :
457 : // Pause the current thread until it's pause flag is unset.
458 : void
459 : PauseCurrentHelperThread();
460 :
461 : // Enqueues a wasm compilation task.
462 : bool
463 : StartOffThreadWasmCompile(wasm::CompileTask* task);
464 :
465 : namespace wasm {
466 :
467 : // Performs MIR optimization and LIR generation on one or several functions.
468 : MOZ_MUST_USE bool
469 : CompileFunction(CompileTask* task, UniqueChars* error);
470 :
471 : }
472 :
473 : /*
474 : * If helper threads are available, start executing the given PromiseTask on a
475 : * helper thread, finishing back on the originating JSContext's owner thread. If
476 : * no helper threads are available, the PromiseTask is synchronously executed
477 : * and finished.
478 : */
479 : bool
480 : StartPromiseTask(JSContext* cx, UniquePtr<PromiseTask> task);
481 :
482 : /*
483 : * Schedule an Ion compilation for a script, given a builder which has been
484 : * generated and read everything needed from the VM state.
485 : */
486 : bool
487 : StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder);
488 :
489 : /*
490 : * Schedule deletion of Ion compilation data.
491 : */
492 : bool
493 : StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock);
494 :
495 : struct AllCompilations {};
496 : struct ZonesInState { JSRuntime* runtime; JS::Zone::GCState state; };
497 :
498 : using CompilationSelector = mozilla::Variant<JSScript*,
499 : JSCompartment*,
500 : ZonesInState,
501 : JSRuntime*,
502 : AllCompilations>;
503 :
504 : /*
505 : * Cancel scheduled or in progress Ion compilations.
506 : */
507 : void
508 : CancelOffThreadIonCompile(const CompilationSelector& selector, bool discardLazyLinkList);
509 :
510 : inline void
511 6 : CancelOffThreadIonCompile(JSScript* script)
512 : {
513 6 : CancelOffThreadIonCompile(CompilationSelector(script), true);
514 6 : }
515 :
516 : inline void
517 : CancelOffThreadIonCompile(JSCompartment* comp)
518 : {
519 : CancelOffThreadIonCompile(CompilationSelector(comp), true);
520 : }
521 :
522 : inline void
523 1 : CancelOffThreadIonCompile(JSRuntime* runtime, JS::Zone::GCState state)
524 : {
525 1 : CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state}), true);
526 1 : }
527 :
528 : inline void
529 4 : CancelOffThreadIonCompile(JSRuntime* runtime)
530 : {
531 4 : CancelOffThreadIonCompile(CompilationSelector(runtime), true);
532 4 : }
533 :
534 : inline void
535 0 : CancelOffThreadIonCompile()
536 : {
537 0 : CancelOffThreadIonCompile(CompilationSelector(AllCompilations()), false);
538 0 : }
539 :
540 : #ifdef DEBUG
541 : bool
542 : HasOffThreadIonCompile(JSCompartment* comp);
543 : #endif
544 :
545 : /* Cancel all scheduled, in progress or finished parses for runtime. */
546 : void
547 : CancelOffThreadParses(JSRuntime* runtime);
548 :
549 : /*
550 : * Start a parse/emit cycle for a stream of source. The characters must stay
551 : * alive until the compilation finishes.
552 : */
553 : bool
554 : StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
555 : const char16_t* chars, size_t length,
556 : JS::OffThreadCompileCallback callback, void* callbackData);
557 :
558 : bool
559 : StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
560 : const char16_t* chars, size_t length,
561 : JS::OffThreadCompileCallback callback, void* callbackData);
562 :
563 : bool
564 : StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
565 : const JS::TranscodeRange& range,
566 : JS::OffThreadCompileCallback callback, void* callbackData);
567 :
568 : bool
569 : StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
570 : JS::TranscodeSources& sources,
571 : JS::OffThreadCompileCallback callback, void* callbackData);
572 :
573 : /*
574 : * Called at the end of GC to enqueue any Parse tasks that were waiting on an
575 : * atoms-zone GC to finish.
576 : */
577 : void
578 : EnqueuePendingParseTasksAfterGC(JSRuntime* rt);
579 :
580 : struct AutoEnqueuePendingParseTasksAfterGC {
581 : const gc::GCRuntime& gc_;
582 3 : explicit AutoEnqueuePendingParseTasksAfterGC(const gc::GCRuntime& gc) : gc_(gc) {}
583 : ~AutoEnqueuePendingParseTasksAfterGC();
584 : };
585 :
586 : // Enqueue a compression job to be processed if there's a major GC.
587 : bool
588 : EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task);
589 :
590 : // Cancel all scheduled, in progress, or finished compression tasks for
591 : // runtime.
592 : void
593 : CancelOffThreadCompressions(JSRuntime* runtime);
594 :
595 491 : class MOZ_RAII AutoLockHelperThreadState : public LockGuard<Mutex>
596 : {
597 : using Base = LockGuard<Mutex>;
598 :
599 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
600 :
601 : public:
602 527 : explicit AutoLockHelperThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
603 527 : : Base(HelperThreadState().helperLock)
604 : {
605 527 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
606 527 : }
607 : };
608 :
609 55 : class MOZ_RAII AutoUnlockHelperThreadState : public UnlockGuard<Mutex>
610 : {
611 : using Base = UnlockGuard<Mutex>;
612 :
613 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
614 :
615 : public:
616 :
617 56 : explicit AutoUnlockHelperThreadState(AutoLockHelperThreadState& locked
618 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
619 56 : : Base(locked)
620 : {
621 56 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
622 56 : }
623 : };
624 :
625 : struct ParseTask
626 : {
627 : ParseTaskKind kind;
628 : OwningCompileOptions options;
629 :
630 : mozilla::Variant<const JS::TranscodeRange,
631 : JS::TwoByteChars,
632 : JS::TranscodeSources*> data;
633 :
634 : LifoAlloc alloc;
635 :
636 : // Rooted pointer to the global object to use while parsing.
637 : JSObject* parseGlobal;
638 :
639 : // Callback invoked off thread when the parse finishes.
640 : JS::OffThreadCompileCallback callback;
641 : void* callbackData;
642 :
643 : // Holds the final scripts between the invocation of the callback and the
644 : // point where FinishOffThreadScript is called, which will destroy the
645 : // ParseTask.
646 : GCVector<JSScript*, 1> scripts;
647 :
648 : // Holds the ScriptSourceObjects generated for the script compilation.
649 : GCVector<ScriptSourceObject*, 1> sourceObjects;
650 :
651 : // Any errors or warnings produced during compilation. These are reported
652 : // when finishing the script.
653 : Vector<CompileError*, 0, SystemAllocPolicy> errors;
654 : bool overRecursed;
655 : bool outOfMemory;
656 :
657 : ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
658 : const char16_t* chars, size_t length,
659 : JS::OffThreadCompileCallback callback, void* callbackData);
660 : ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
661 : const JS::TranscodeRange& range,
662 : JS::OffThreadCompileCallback callback, void* callbackData);
663 : ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
664 : JS::TranscodeSources& sources,
665 : JS::OffThreadCompileCallback callback, void* callbackData);
666 : bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
667 :
668 : void activate(JSRuntime* rt);
669 : virtual void parse(JSContext* cx) = 0;
670 : bool finish(JSContext* cx);
671 :
672 0 : bool runtimeMatches(JSRuntime* rt) {
673 0 : return parseGlobal->runtimeFromAnyThread() == rt;
674 : }
675 :
676 : virtual ~ParseTask();
677 :
678 : void trace(JSTracer* trc);
679 : };
680 :
681 0 : struct ScriptParseTask : public ParseTask
682 : {
683 : ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
684 : const char16_t* chars, size_t length,
685 : JS::OffThreadCompileCallback callback, void* callbackData);
686 : void parse(JSContext* cx) override;
687 : };
688 :
689 0 : struct ModuleParseTask : public ParseTask
690 : {
691 : ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
692 : const char16_t* chars, size_t length,
693 : JS::OffThreadCompileCallback callback, void* callbackData);
694 : void parse(JSContext* cx) override;
695 : };
696 :
697 0 : struct ScriptDecodeTask : public ParseTask
698 : {
699 : ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
700 : const JS::TranscodeRange& range,
701 : JS::OffThreadCompileCallback callback, void* callbackData);
702 : void parse(JSContext* cx) override;
703 : };
704 :
705 15 : struct MultiScriptsDecodeTask : public ParseTask
706 : {
707 : MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
708 : JS::TranscodeSources& sources,
709 : JS::OffThreadCompileCallback callback, void* callbackData);
710 : void parse(JSContext* cx) override;
711 : };
712 :
713 : // Return whether, if a new parse task was started, it would need to wait for
714 : // an in-progress GC to complete before starting.
715 : extern bool
716 : OffThreadParsingMustWaitForGC(JSRuntime* rt);
717 :
718 : // It is not desirable to eagerly compress: if lazy functions that are tied to
719 : // the ScriptSource were to be executed relatively soon after parsing, they
720 : // would need to block on decompression, which hurts responsiveness.
721 : //
722 : // To this end, compression tasks are heap allocated and enqueued in a pending
723 : // list by ScriptSource::setSourceCopy. When a major GC occurs, we schedule
724 : // pending compression tasks and move the ones that are ready to be compressed
725 : // to the worklist. Currently, a compression task is considered ready 2 major
726 : // GCs after being enqueued. Completed tasks are handled during the sweeping
727 : // phase by AttachCompressedSourcesTask, which runs in parallel with other GC
728 : // sweeping tasks.
729 0 : class SourceCompressionTask
730 : {
731 : friend struct HelperThread;
732 : friend class ScriptSource;
733 :
734 : // The runtime that the ScriptSource is associated with, in the sense that
735 : // it uses the runtime's immutable string cache.
736 : JSRuntime* runtime_;
737 :
738 : // The major GC number of the runtime when the task was enqueued.
739 : uint64_t majorGCNumber_;
740 :
741 : // The source to be compressed.
742 : ScriptSourceHolder sourceHolder_;
743 :
744 : // The resultant compressed string. If the compressed string is larger
745 : // than the original, or we OOM'd during compression, or nothing else
746 : // except the task is holding the ScriptSource alive when scheduled to
747 : // compress, this will remain None upon completion.
748 : mozilla::Maybe<SharedImmutableString> resultString_;
749 :
750 : public:
751 : // The majorGCNumber is used for scheduling tasks.
752 155 : SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
753 155 : : runtime_(rt),
754 155 : majorGCNumber_(rt->gc.majorGCCount()),
755 310 : sourceHolder_(source)
756 155 : { }
757 :
758 0 : bool runtimeMatches(JSRuntime* runtime) const {
759 0 : return runtime == runtime_;
760 : }
761 49 : bool shouldStart() const {
762 : // We wait 2 major GCs to start compressing, in order to avoid
763 : // immediate compression.
764 49 : return runtime_->gc.majorGCCount() > majorGCNumber_ + 1;
765 : }
766 :
767 0 : bool shouldCancel() const {
768 : // If the refcount is exactly 1, then nothing else is holding on to the
769 : // ScriptSource, so no reason to compress it and we should cancel the task.
770 0 : return sourceHolder_.get()->refs == 1;
771 : }
772 :
773 : void work();
774 : void complete();
775 : };
776 :
777 : } /* namespace js */
778 :
779 : #endif /* vm_HelperThreads_h */
|