Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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/. */
7 /*
8 * Definitions for managing off-main-thread work using a shared, per runtime
9 * worklist. Worklist items are engine internal, and are distinct from e.g.
10 * web workers.
11 */
13 #ifndef jsworkers_h
14 #define jsworkers_h
16 #include "mozilla/GuardObjects.h"
17 #include "mozilla/PodOperations.h"
19 #include "jscntxt.h"
20 #include "jslock.h"
22 #include "frontend/TokenStream.h"
23 #include "jit/Ion.h"
25 namespace js {
27 struct WorkerThread;
28 struct AsmJSParallelTask;
29 struct ParseTask;
30 namespace jit {
31 class IonBuilder;
32 }
34 #ifdef JS_THREADSAFE
36 // Per-process state for off thread work items.
37 class GlobalWorkerThreadState
38 {
39 public:
40 // Number of CPUs to treat this machine as having when creating threads.
41 // May be accessed without locking.
42 size_t cpuCount;
44 // Number of threads to create. May be accessed without locking.
45 size_t threadCount;
47 typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
48 typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
49 typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
50 typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
52 // List of available threads, or null if the thread state has not been initialized.
53 WorkerThread *threads;
55 private:
56 // The lists below are all protected by |lock|.
58 // Ion compilation worklist and finished jobs.
59 IonBuilderVector ionWorklist_, ionFinishedList_;
61 // AsmJS worklist and finished jobs.
62 //
63 // Simultaneous AsmJS compilations all service the same AsmJS module.
64 // The main thread must pick up finished optimizations and perform codegen.
65 // |asmJSCompilationInProgress| is used to avoid triggering compilations
66 // for more than one module at a time.
67 AsmJSParallelTaskVector asmJSWorklist_, asmJSFinishedList_;
69 public:
70 // For now, only allow a single parallel asm.js compilation to happen at a
71 // time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc.
72 mozilla::Atomic<bool> asmJSCompilationInProgress;
74 private:
75 // Script parsing/emitting worklist and finished jobs.
76 ParseTaskVector parseWorklist_, parseFinishedList_;
78 // Parse tasks waiting for an atoms-zone GC to complete.
79 ParseTaskVector parseWaitingOnGC_;
81 // Source compression worklist.
82 SourceCompressionTaskVector compressionWorklist_;
84 public:
85 GlobalWorkerThreadState();
87 void ensureInitialized();
88 void finish();
90 void lock();
91 void unlock();
93 # ifdef DEBUG
94 bool isLocked();
95 # endif
97 enum CondVar {
98 // For notifying threads waiting for work that they may be able to make progress.
99 CONSUMER,
101 // For notifying threads doing work that they may be able to make progress.
102 PRODUCER
103 };
105 void wait(CondVar which, uint32_t timeoutMillis = 0);
106 void notifyAll(CondVar which);
107 void notifyOne(CondVar which);
109 // Helper method for removing items from the vectors below while iterating over them.
110 template <typename T>
111 void remove(T &vector, size_t *index)
112 {
113 vector[(*index)--] = vector.back();
114 vector.popBack();
115 }
117 IonBuilderVector &ionWorklist() {
118 JS_ASSERT(isLocked());
119 return ionWorklist_;
120 }
121 IonBuilderVector &ionFinishedList() {
122 JS_ASSERT(isLocked());
123 return ionFinishedList_;
124 }
126 AsmJSParallelTaskVector &asmJSWorklist() {
127 JS_ASSERT(isLocked());
128 return asmJSWorklist_;
129 }
130 AsmJSParallelTaskVector &asmJSFinishedList() {
131 JS_ASSERT(isLocked());
132 return asmJSFinishedList_;
133 }
135 ParseTaskVector &parseWorklist() {
136 JS_ASSERT(isLocked());
137 return parseWorklist_;
138 }
139 ParseTaskVector &parseFinishedList() {
140 JS_ASSERT(isLocked());
141 return parseFinishedList_;
142 }
143 ParseTaskVector &parseWaitingOnGC() {
144 JS_ASSERT(isLocked());
145 return parseWaitingOnGC_;
146 }
148 SourceCompressionTaskVector &compressionWorklist() {
149 JS_ASSERT(isLocked());
150 return compressionWorklist_;
151 }
153 bool canStartAsmJSCompile();
154 bool canStartIonCompile();
155 bool canStartParseTask();
156 bool canStartCompressionTask();
158 uint32_t harvestFailedAsmJSJobs() {
159 JS_ASSERT(isLocked());
160 uint32_t n = numAsmJSFailedJobs;
161 numAsmJSFailedJobs = 0;
162 return n;
163 }
164 void noteAsmJSFailure(void *func) {
165 // Be mindful to signal the main thread after calling this function.
166 JS_ASSERT(isLocked());
167 if (!asmJSFailedFunction)
168 asmJSFailedFunction = func;
169 numAsmJSFailedJobs++;
170 }
171 bool asmJSWorkerFailed() const {
172 return bool(numAsmJSFailedJobs);
173 }
174 void resetAsmJSFailureState() {
175 numAsmJSFailedJobs = 0;
176 asmJSFailedFunction = nullptr;
177 }
178 void *maybeAsmJSFailedFunction() const {
179 return asmJSFailedFunction;
180 }
182 JSScript *finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token);
183 bool compressionInProgress(SourceCompressionTask *task);
184 SourceCompressionTask *compressionTaskForSource(ScriptSource *ss);
186 private:
188 /*
189 * Lock protecting all mutable shared state accessed by helper threads, and
190 * used by all condition variables.
191 */
192 PRLock *workerLock;
194 # ifdef DEBUG
195 PRThread *lockOwner;
196 # endif
198 /* Condvars for threads waiting/notifying each other. */
199 PRCondVar *consumerWakeup;
200 PRCondVar *producerWakeup;
202 /*
203 * Number of AsmJS workers that encountered failure for the active module.
204 * Their parent is logically the main thread, and this number serves for harvesting.
205 */
206 uint32_t numAsmJSFailedJobs;
208 /*
209 * Function index |i| in |Module.function(i)| of first failed AsmJS function.
210 * -1 if no function has failed.
211 */
212 void *asmJSFailedFunction;
213 };
215 static inline GlobalWorkerThreadState &
216 WorkerThreadState()
217 {
218 extern GlobalWorkerThreadState gWorkerThreadState;
219 return gWorkerThreadState;
220 }
222 /* Individual helper thread, one allocated per core. */
223 struct WorkerThread
224 {
225 mozilla::Maybe<PerThreadData> threadData;
226 PRThread *thread;
228 /* Indicate to an idle thread that it should finish executing. */
229 bool terminate;
231 /* Any builder currently being compiled by Ion on this thread. */
232 jit::IonBuilder *ionBuilder;
234 /* Any AsmJS data currently being optimized by Ion on this thread. */
235 AsmJSParallelTask *asmData;
237 /* Any source being parsed/emitted on this thread. */
238 ParseTask *parseTask;
240 /* Any source being compressed on this thread. */
241 SourceCompressionTask *compressionTask;
243 bool idle() const {
244 return !ionBuilder && !asmData && !parseTask && !compressionTask;
245 }
247 void destroy();
249 void handleAsmJSWorkload();
250 void handleIonWorkload();
251 void handleParseWorkload();
252 void handleCompressionWorkload();
254 static void ThreadMain(void *arg);
255 void threadLoop();
256 };
258 #endif /* JS_THREADSAFE */
260 /* Methods for interacting with worker threads. */
262 // Initialize worker threads unless already initialized.
263 void
264 EnsureWorkerThreadsInitialized(ExclusiveContext *cx);
266 // This allows the JS shell to override GetCPUCount() when passed the
267 // --thread-count=N option.
268 void
269 SetFakeCPUCount(size_t count);
271 #ifdef JS_ION
273 /* Perform MIR optimization and LIR generation on a single function. */
274 bool
275 StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData);
277 /*
278 * Schedule an Ion compilation for a script, given a builder which has been
279 * generated and read everything needed from the VM state.
280 */
281 bool
282 StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder);
284 #endif // JS_ION
286 /*
287 * Cancel a scheduled or in progress Ion compilation for script. If script is
288 * nullptr, all compilations for the compartment are cancelled.
289 */
290 void
291 CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
293 /* Cancel all scheduled, in progress or finished parses for runtime. */
294 void
295 CancelOffThreadParses(JSRuntime *runtime);
297 /*
298 * Start a parse/emit cycle for a stream of source. The characters must stay
299 * alive until the compilation finishes.
300 */
301 bool
302 StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
303 const jschar *chars, size_t length,
304 JS::OffThreadCompileCallback callback, void *callbackData);
306 /*
307 * Called at the end of GC to enqueue any Parse tasks that were waiting on an
308 * atoms-zone GC to finish.
309 */
310 void
311 EnqueuePendingParseTasksAfterGC(JSRuntime *rt);
313 /* Start a compression job for the specified token. */
314 bool
315 StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
317 class AutoLockWorkerThreadState
318 {
319 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
321 #ifdef JS_THREADSAFE
322 public:
323 AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
324 {
325 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
326 WorkerThreadState().lock();
327 }
329 ~AutoLockWorkerThreadState() {
330 WorkerThreadState().unlock();
331 }
332 #else
333 public:
334 AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
335 {
336 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
337 }
338 #endif
339 };
341 class AutoUnlockWorkerThreadState
342 {
343 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
345 public:
347 AutoUnlockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
348 {
349 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
350 #ifdef JS_THREADSAFE
351 WorkerThreadState().unlock();
352 #endif
353 }
355 ~AutoUnlockWorkerThreadState()
356 {
357 #ifdef JS_THREADSAFE
358 WorkerThreadState().lock();
359 #endif
360 }
361 };
363 #ifdef JS_ION
364 struct AsmJSParallelTask
365 {
366 JSRuntime *runtime; // Associated runtime.
367 LifoAlloc lifo; // Provider of all heap memory used for compilation.
368 void *func; // Really, a ModuleCompiler::Func*
369 jit::MIRGenerator *mir; // Passed from main thread to worker.
370 jit::LIRGraph *lir; // Passed from worker to main thread.
371 unsigned compileTime;
373 AsmJSParallelTask(size_t defaultChunkSize)
374 : runtime(nullptr), lifo(defaultChunkSize), func(nullptr), mir(nullptr), lir(nullptr), compileTime(0)
375 { }
377 void init(JSRuntime *rt, void *func, jit::MIRGenerator *mir) {
378 this->runtime = rt;
379 this->func = func;
380 this->mir = mir;
381 this->lir = nullptr;
382 }
383 };
384 #endif
386 struct ParseTask
387 {
388 ExclusiveContext *cx;
389 OwningCompileOptions options;
390 const jschar *chars;
391 size_t length;
392 LifoAlloc alloc;
394 // Rooted pointer to the global object used by 'cx'.
395 PersistentRootedObject exclusiveContextGlobal;
397 // Saved GC-managed CompileOptions fields that will populate slots in
398 // the ScriptSourceObject. We create the ScriptSourceObject in the
399 // compilation's temporary compartment, so storing these values there
400 // at that point would create cross-compartment references. Instead we
401 // hold them here, and install them after merging the compartments.
402 PersistentRootedObject optionsElement;
403 PersistentRootedScript optionsIntroductionScript;
405 // Callback invoked off the main thread when the parse finishes.
406 JS::OffThreadCompileCallback callback;
407 void *callbackData;
409 // Holds the final script between the invocation of the callback and the
410 // point where FinishOffThreadScript is called, which will destroy the
411 // ParseTask.
412 JSScript *script;
414 // Any errors or warnings produced during compilation. These are reported
415 // when finishing the script.
416 Vector<frontend::CompileError *> errors;
417 bool overRecursed;
419 ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal,
420 JSContext *initCx, const jschar *chars, size_t length,
421 JS::OffThreadCompileCallback callback, void *callbackData);
422 bool init(JSContext *cx, const ReadOnlyCompileOptions &options);
424 void activate(JSRuntime *rt);
425 void finish();
427 bool runtimeMatches(JSRuntime *rt) {
428 return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
429 }
431 ~ParseTask();
432 };
434 #ifdef JS_THREADSAFE
435 // Return whether, if a new parse task was started, it would need to wait for
436 // an in-progress GC to complete before starting.
437 extern bool
438 OffThreadParsingMustWaitForGC(JSRuntime *rt);
439 #endif
441 // Compression tasks are allocated on the stack by their triggering thread,
442 // which will block on the compression completing as the task goes out of scope
443 // to ensure it completes at the required time.
444 struct SourceCompressionTask
445 {
446 friend class ScriptSource;
448 #ifdef JS_THREADSAFE
449 // Thread performing the compression.
450 WorkerThread *workerThread;
451 #endif
453 private:
454 // Context from the triggering thread. Don't use this off thread!
455 ExclusiveContext *cx;
457 ScriptSource *ss;
458 const jschar *chars;
459 bool oom;
461 // Atomic flag to indicate to a worker thread that it should abort
462 // compression on the source.
463 mozilla::Atomic<bool, mozilla::Relaxed> abort_;
465 public:
466 explicit SourceCompressionTask(ExclusiveContext *cx)
467 : cx(cx), ss(nullptr), chars(nullptr), oom(false), abort_(false)
468 {
469 #ifdef JS_THREADSAFE
470 workerThread = nullptr;
471 #endif
472 }
474 ~SourceCompressionTask()
475 {
476 complete();
477 }
479 bool work();
480 bool complete();
481 void abort() { abort_ = true; }
482 bool active() const { return !!ss; }
483 ScriptSource *source() { return ss; }
484 const jschar *uncompressedChars() { return chars; }
485 void setOOM() { oom = true; }
486 };
488 } /* namespace js */
490 #endif /* jsworkers_h */