js/src/jsworkers.h

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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

mercurial