1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsworkers.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,490 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * Definitions for managing off-main-thread work using a shared, per runtime 1.12 + * worklist. Worklist items are engine internal, and are distinct from e.g. 1.13 + * web workers. 1.14 + */ 1.15 + 1.16 +#ifndef jsworkers_h 1.17 +#define jsworkers_h 1.18 + 1.19 +#include "mozilla/GuardObjects.h" 1.20 +#include "mozilla/PodOperations.h" 1.21 + 1.22 +#include "jscntxt.h" 1.23 +#include "jslock.h" 1.24 + 1.25 +#include "frontend/TokenStream.h" 1.26 +#include "jit/Ion.h" 1.27 + 1.28 +namespace js { 1.29 + 1.30 +struct WorkerThread; 1.31 +struct AsmJSParallelTask; 1.32 +struct ParseTask; 1.33 +namespace jit { 1.34 + class IonBuilder; 1.35 +} 1.36 + 1.37 +#ifdef JS_THREADSAFE 1.38 + 1.39 +// Per-process state for off thread work items. 1.40 +class GlobalWorkerThreadState 1.41 +{ 1.42 + public: 1.43 + // Number of CPUs to treat this machine as having when creating threads. 1.44 + // May be accessed without locking. 1.45 + size_t cpuCount; 1.46 + 1.47 + // Number of threads to create. May be accessed without locking. 1.48 + size_t threadCount; 1.49 + 1.50 + typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector; 1.51 + typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector; 1.52 + typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector; 1.53 + typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector; 1.54 + 1.55 + // List of available threads, or null if the thread state has not been initialized. 1.56 + WorkerThread *threads; 1.57 + 1.58 + private: 1.59 + // The lists below are all protected by |lock|. 1.60 + 1.61 + // Ion compilation worklist and finished jobs. 1.62 + IonBuilderVector ionWorklist_, ionFinishedList_; 1.63 + 1.64 + // AsmJS worklist and finished jobs. 1.65 + // 1.66 + // Simultaneous AsmJS compilations all service the same AsmJS module. 1.67 + // The main thread must pick up finished optimizations and perform codegen. 1.68 + // |asmJSCompilationInProgress| is used to avoid triggering compilations 1.69 + // for more than one module at a time. 1.70 + AsmJSParallelTaskVector asmJSWorklist_, asmJSFinishedList_; 1.71 + 1.72 + public: 1.73 + // For now, only allow a single parallel asm.js compilation to happen at a 1.74 + // time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc. 1.75 + mozilla::Atomic<bool> asmJSCompilationInProgress; 1.76 + 1.77 + private: 1.78 + // Script parsing/emitting worklist and finished jobs. 1.79 + ParseTaskVector parseWorklist_, parseFinishedList_; 1.80 + 1.81 + // Parse tasks waiting for an atoms-zone GC to complete. 1.82 + ParseTaskVector parseWaitingOnGC_; 1.83 + 1.84 + // Source compression worklist. 1.85 + SourceCompressionTaskVector compressionWorklist_; 1.86 + 1.87 + public: 1.88 + GlobalWorkerThreadState(); 1.89 + 1.90 + void ensureInitialized(); 1.91 + void finish(); 1.92 + 1.93 + void lock(); 1.94 + void unlock(); 1.95 + 1.96 +# ifdef DEBUG 1.97 + bool isLocked(); 1.98 +# endif 1.99 + 1.100 + enum CondVar { 1.101 + // For notifying threads waiting for work that they may be able to make progress. 1.102 + CONSUMER, 1.103 + 1.104 + // For notifying threads doing work that they may be able to make progress. 1.105 + PRODUCER 1.106 + }; 1.107 + 1.108 + void wait(CondVar which, uint32_t timeoutMillis = 0); 1.109 + void notifyAll(CondVar which); 1.110 + void notifyOne(CondVar which); 1.111 + 1.112 + // Helper method for removing items from the vectors below while iterating over them. 1.113 + template <typename T> 1.114 + void remove(T &vector, size_t *index) 1.115 + { 1.116 + vector[(*index)--] = vector.back(); 1.117 + vector.popBack(); 1.118 + } 1.119 + 1.120 + IonBuilderVector &ionWorklist() { 1.121 + JS_ASSERT(isLocked()); 1.122 + return ionWorklist_; 1.123 + } 1.124 + IonBuilderVector &ionFinishedList() { 1.125 + JS_ASSERT(isLocked()); 1.126 + return ionFinishedList_; 1.127 + } 1.128 + 1.129 + AsmJSParallelTaskVector &asmJSWorklist() { 1.130 + JS_ASSERT(isLocked()); 1.131 + return asmJSWorklist_; 1.132 + } 1.133 + AsmJSParallelTaskVector &asmJSFinishedList() { 1.134 + JS_ASSERT(isLocked()); 1.135 + return asmJSFinishedList_; 1.136 + } 1.137 + 1.138 + ParseTaskVector &parseWorklist() { 1.139 + JS_ASSERT(isLocked()); 1.140 + return parseWorklist_; 1.141 + } 1.142 + ParseTaskVector &parseFinishedList() { 1.143 + JS_ASSERT(isLocked()); 1.144 + return parseFinishedList_; 1.145 + } 1.146 + ParseTaskVector &parseWaitingOnGC() { 1.147 + JS_ASSERT(isLocked()); 1.148 + return parseWaitingOnGC_; 1.149 + } 1.150 + 1.151 + SourceCompressionTaskVector &compressionWorklist() { 1.152 + JS_ASSERT(isLocked()); 1.153 + return compressionWorklist_; 1.154 + } 1.155 + 1.156 + bool canStartAsmJSCompile(); 1.157 + bool canStartIonCompile(); 1.158 + bool canStartParseTask(); 1.159 + bool canStartCompressionTask(); 1.160 + 1.161 + uint32_t harvestFailedAsmJSJobs() { 1.162 + JS_ASSERT(isLocked()); 1.163 + uint32_t n = numAsmJSFailedJobs; 1.164 + numAsmJSFailedJobs = 0; 1.165 + return n; 1.166 + } 1.167 + void noteAsmJSFailure(void *func) { 1.168 + // Be mindful to signal the main thread after calling this function. 1.169 + JS_ASSERT(isLocked()); 1.170 + if (!asmJSFailedFunction) 1.171 + asmJSFailedFunction = func; 1.172 + numAsmJSFailedJobs++; 1.173 + } 1.174 + bool asmJSWorkerFailed() const { 1.175 + return bool(numAsmJSFailedJobs); 1.176 + } 1.177 + void resetAsmJSFailureState() { 1.178 + numAsmJSFailedJobs = 0; 1.179 + asmJSFailedFunction = nullptr; 1.180 + } 1.181 + void *maybeAsmJSFailedFunction() const { 1.182 + return asmJSFailedFunction; 1.183 + } 1.184 + 1.185 + JSScript *finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token); 1.186 + bool compressionInProgress(SourceCompressionTask *task); 1.187 + SourceCompressionTask *compressionTaskForSource(ScriptSource *ss); 1.188 + 1.189 + private: 1.190 + 1.191 + /* 1.192 + * Lock protecting all mutable shared state accessed by helper threads, and 1.193 + * used by all condition variables. 1.194 + */ 1.195 + PRLock *workerLock; 1.196 + 1.197 +# ifdef DEBUG 1.198 + PRThread *lockOwner; 1.199 +# endif 1.200 + 1.201 + /* Condvars for threads waiting/notifying each other. */ 1.202 + PRCondVar *consumerWakeup; 1.203 + PRCondVar *producerWakeup; 1.204 + 1.205 + /* 1.206 + * Number of AsmJS workers that encountered failure for the active module. 1.207 + * Their parent is logically the main thread, and this number serves for harvesting. 1.208 + */ 1.209 + uint32_t numAsmJSFailedJobs; 1.210 + 1.211 + /* 1.212 + * Function index |i| in |Module.function(i)| of first failed AsmJS function. 1.213 + * -1 if no function has failed. 1.214 + */ 1.215 + void *asmJSFailedFunction; 1.216 +}; 1.217 + 1.218 +static inline GlobalWorkerThreadState & 1.219 +WorkerThreadState() 1.220 +{ 1.221 + extern GlobalWorkerThreadState gWorkerThreadState; 1.222 + return gWorkerThreadState; 1.223 +} 1.224 + 1.225 +/* Individual helper thread, one allocated per core. */ 1.226 +struct WorkerThread 1.227 +{ 1.228 + mozilla::Maybe<PerThreadData> threadData; 1.229 + PRThread *thread; 1.230 + 1.231 + /* Indicate to an idle thread that it should finish executing. */ 1.232 + bool terminate; 1.233 + 1.234 + /* Any builder currently being compiled by Ion on this thread. */ 1.235 + jit::IonBuilder *ionBuilder; 1.236 + 1.237 + /* Any AsmJS data currently being optimized by Ion on this thread. */ 1.238 + AsmJSParallelTask *asmData; 1.239 + 1.240 + /* Any source being parsed/emitted on this thread. */ 1.241 + ParseTask *parseTask; 1.242 + 1.243 + /* Any source being compressed on this thread. */ 1.244 + SourceCompressionTask *compressionTask; 1.245 + 1.246 + bool idle() const { 1.247 + return !ionBuilder && !asmData && !parseTask && !compressionTask; 1.248 + } 1.249 + 1.250 + void destroy(); 1.251 + 1.252 + void handleAsmJSWorkload(); 1.253 + void handleIonWorkload(); 1.254 + void handleParseWorkload(); 1.255 + void handleCompressionWorkload(); 1.256 + 1.257 + static void ThreadMain(void *arg); 1.258 + void threadLoop(); 1.259 +}; 1.260 + 1.261 +#endif /* JS_THREADSAFE */ 1.262 + 1.263 +/* Methods for interacting with worker threads. */ 1.264 + 1.265 +// Initialize worker threads unless already initialized. 1.266 +void 1.267 +EnsureWorkerThreadsInitialized(ExclusiveContext *cx); 1.268 + 1.269 +// This allows the JS shell to override GetCPUCount() when passed the 1.270 +// --thread-count=N option. 1.271 +void 1.272 +SetFakeCPUCount(size_t count); 1.273 + 1.274 +#ifdef JS_ION 1.275 + 1.276 +/* Perform MIR optimization and LIR generation on a single function. */ 1.277 +bool 1.278 +StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData); 1.279 + 1.280 +/* 1.281 + * Schedule an Ion compilation for a script, given a builder which has been 1.282 + * generated and read everything needed from the VM state. 1.283 + */ 1.284 +bool 1.285 +StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder); 1.286 + 1.287 +#endif // JS_ION 1.288 + 1.289 +/* 1.290 + * Cancel a scheduled or in progress Ion compilation for script. If script is 1.291 + * nullptr, all compilations for the compartment are cancelled. 1.292 + */ 1.293 +void 1.294 +CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script); 1.295 + 1.296 +/* Cancel all scheduled, in progress or finished parses for runtime. */ 1.297 +void 1.298 +CancelOffThreadParses(JSRuntime *runtime); 1.299 + 1.300 +/* 1.301 + * Start a parse/emit cycle for a stream of source. The characters must stay 1.302 + * alive until the compilation finishes. 1.303 + */ 1.304 +bool 1.305 +StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options, 1.306 + const jschar *chars, size_t length, 1.307 + JS::OffThreadCompileCallback callback, void *callbackData); 1.308 + 1.309 +/* 1.310 + * Called at the end of GC to enqueue any Parse tasks that were waiting on an 1.311 + * atoms-zone GC to finish. 1.312 + */ 1.313 +void 1.314 +EnqueuePendingParseTasksAfterGC(JSRuntime *rt); 1.315 + 1.316 +/* Start a compression job for the specified token. */ 1.317 +bool 1.318 +StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task); 1.319 + 1.320 +class AutoLockWorkerThreadState 1.321 +{ 1.322 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.323 + 1.324 +#ifdef JS_THREADSAFE 1.325 + public: 1.326 + AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) 1.327 + { 1.328 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.329 + WorkerThreadState().lock(); 1.330 + } 1.331 + 1.332 + ~AutoLockWorkerThreadState() { 1.333 + WorkerThreadState().unlock(); 1.334 + } 1.335 +#else 1.336 + public: 1.337 + AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) 1.338 + { 1.339 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.340 + } 1.341 +#endif 1.342 +}; 1.343 + 1.344 +class AutoUnlockWorkerThreadState 1.345 +{ 1.346 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.347 + 1.348 + public: 1.349 + 1.350 + AutoUnlockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) 1.351 + { 1.352 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.353 +#ifdef JS_THREADSAFE 1.354 + WorkerThreadState().unlock(); 1.355 +#endif 1.356 + } 1.357 + 1.358 + ~AutoUnlockWorkerThreadState() 1.359 + { 1.360 +#ifdef JS_THREADSAFE 1.361 + WorkerThreadState().lock(); 1.362 +#endif 1.363 + } 1.364 +}; 1.365 + 1.366 +#ifdef JS_ION 1.367 +struct AsmJSParallelTask 1.368 +{ 1.369 + JSRuntime *runtime; // Associated runtime. 1.370 + LifoAlloc lifo; // Provider of all heap memory used for compilation. 1.371 + void *func; // Really, a ModuleCompiler::Func* 1.372 + jit::MIRGenerator *mir; // Passed from main thread to worker. 1.373 + jit::LIRGraph *lir; // Passed from worker to main thread. 1.374 + unsigned compileTime; 1.375 + 1.376 + AsmJSParallelTask(size_t defaultChunkSize) 1.377 + : runtime(nullptr), lifo(defaultChunkSize), func(nullptr), mir(nullptr), lir(nullptr), compileTime(0) 1.378 + { } 1.379 + 1.380 + void init(JSRuntime *rt, void *func, jit::MIRGenerator *mir) { 1.381 + this->runtime = rt; 1.382 + this->func = func; 1.383 + this->mir = mir; 1.384 + this->lir = nullptr; 1.385 + } 1.386 +}; 1.387 +#endif 1.388 + 1.389 +struct ParseTask 1.390 +{ 1.391 + ExclusiveContext *cx; 1.392 + OwningCompileOptions options; 1.393 + const jschar *chars; 1.394 + size_t length; 1.395 + LifoAlloc alloc; 1.396 + 1.397 + // Rooted pointer to the global object used by 'cx'. 1.398 + PersistentRootedObject exclusiveContextGlobal; 1.399 + 1.400 + // Saved GC-managed CompileOptions fields that will populate slots in 1.401 + // the ScriptSourceObject. We create the ScriptSourceObject in the 1.402 + // compilation's temporary compartment, so storing these values there 1.403 + // at that point would create cross-compartment references. Instead we 1.404 + // hold them here, and install them after merging the compartments. 1.405 + PersistentRootedObject optionsElement; 1.406 + PersistentRootedScript optionsIntroductionScript; 1.407 + 1.408 + // Callback invoked off the main thread when the parse finishes. 1.409 + JS::OffThreadCompileCallback callback; 1.410 + void *callbackData; 1.411 + 1.412 + // Holds the final script between the invocation of the callback and the 1.413 + // point where FinishOffThreadScript is called, which will destroy the 1.414 + // ParseTask. 1.415 + JSScript *script; 1.416 + 1.417 + // Any errors or warnings produced during compilation. These are reported 1.418 + // when finishing the script. 1.419 + Vector<frontend::CompileError *> errors; 1.420 + bool overRecursed; 1.421 + 1.422 + ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, 1.423 + JSContext *initCx, const jschar *chars, size_t length, 1.424 + JS::OffThreadCompileCallback callback, void *callbackData); 1.425 + bool init(JSContext *cx, const ReadOnlyCompileOptions &options); 1.426 + 1.427 + void activate(JSRuntime *rt); 1.428 + void finish(); 1.429 + 1.430 + bool runtimeMatches(JSRuntime *rt) { 1.431 + return exclusiveContextGlobal->runtimeFromAnyThread() == rt; 1.432 + } 1.433 + 1.434 + ~ParseTask(); 1.435 +}; 1.436 + 1.437 +#ifdef JS_THREADSAFE 1.438 +// Return whether, if a new parse task was started, it would need to wait for 1.439 +// an in-progress GC to complete before starting. 1.440 +extern bool 1.441 +OffThreadParsingMustWaitForGC(JSRuntime *rt); 1.442 +#endif 1.443 + 1.444 +// Compression tasks are allocated on the stack by their triggering thread, 1.445 +// which will block on the compression completing as the task goes out of scope 1.446 +// to ensure it completes at the required time. 1.447 +struct SourceCompressionTask 1.448 +{ 1.449 + friend class ScriptSource; 1.450 + 1.451 +#ifdef JS_THREADSAFE 1.452 + // Thread performing the compression. 1.453 + WorkerThread *workerThread; 1.454 +#endif 1.455 + 1.456 + private: 1.457 + // Context from the triggering thread. Don't use this off thread! 1.458 + ExclusiveContext *cx; 1.459 + 1.460 + ScriptSource *ss; 1.461 + const jschar *chars; 1.462 + bool oom; 1.463 + 1.464 + // Atomic flag to indicate to a worker thread that it should abort 1.465 + // compression on the source. 1.466 + mozilla::Atomic<bool, mozilla::Relaxed> abort_; 1.467 + 1.468 + public: 1.469 + explicit SourceCompressionTask(ExclusiveContext *cx) 1.470 + : cx(cx), ss(nullptr), chars(nullptr), oom(false), abort_(false) 1.471 + { 1.472 +#ifdef JS_THREADSAFE 1.473 + workerThread = nullptr; 1.474 +#endif 1.475 + } 1.476 + 1.477 + ~SourceCompressionTask() 1.478 + { 1.479 + complete(); 1.480 + } 1.481 + 1.482 + bool work(); 1.483 + bool complete(); 1.484 + void abort() { abort_ = true; } 1.485 + bool active() const { return !!ss; } 1.486 + ScriptSource *source() { return ss; } 1.487 + const jschar *uncompressedChars() { return chars; } 1.488 + void setOOM() { oom = true; } 1.489 +}; 1.490 + 1.491 +} /* namespace js */ 1.492 + 1.493 +#endif /* jsworkers_h */