зеркало из https://github.com/mozilla/gecko-dev.git
484 строки
13 KiB
C++
484 строки
13 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* Definitions for managing off-main-thread work using a shared, per runtime
|
|
* worklist. Worklist items are engine internal, and are distinct from e.g.
|
|
* web workers.
|
|
*/
|
|
|
|
#ifndef jsworkers_h
|
|
#define jsworkers_h
|
|
|
|
#include "mozilla/GuardObjects.h"
|
|
#include "mozilla/PodOperations.h"
|
|
|
|
#include "jscntxt.h"
|
|
#include "jslock.h"
|
|
|
|
#include "frontend/TokenStream.h"
|
|
#include "jit/Ion.h"
|
|
|
|
namespace js {
|
|
|
|
struct WorkerThread;
|
|
struct AsmJSParallelTask;
|
|
struct ParseTask;
|
|
namespace jit {
|
|
class IonBuilder;
|
|
}
|
|
|
|
#ifdef JS_WORKER_THREADS
|
|
|
|
/* Per-runtime state for off thread work items. */
|
|
class WorkerThreadState
|
|
{
|
|
public:
|
|
/* Available threads. */
|
|
WorkerThread *threads;
|
|
size_t numThreads;
|
|
|
|
/*
|
|
* Whether all worker threads thread should pause their activity. This acts
|
|
* like the runtime's interrupt field and may be read without locking.
|
|
*/
|
|
volatile size_t shouldPause;
|
|
|
|
/* After shouldPause is set, the number of threads which are paused. */
|
|
uint32_t numPaused;
|
|
|
|
enum CondVar {
|
|
/* For notifying threads waiting for work that they may be able to make progress. */
|
|
CONSUMER,
|
|
|
|
/* For notifying threads doing work that they may be able to make progress. */
|
|
PRODUCER
|
|
};
|
|
|
|
/* Shared worklist for Ion worker threads. */
|
|
Vector<jit::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
|
|
|
|
/* Worklist for AsmJS worker threads. */
|
|
Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSWorklist;
|
|
|
|
/*
|
|
* Finished list for AsmJS worker threads.
|
|
* Simultaneous AsmJS compilations all service the same AsmJS module.
|
|
* The main thread must pick up finished optimizations and perform codegen.
|
|
*/
|
|
Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSFinishedList;
|
|
|
|
/*
|
|
* For now, only allow a single parallel asm.js compilation to happen at a
|
|
* time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc.
|
|
*/
|
|
mozilla::Atomic<uint32_t> asmJSCompilationInProgress;
|
|
|
|
/* Shared worklist for parsing/emitting scripts on worker threads. */
|
|
Vector<ParseTask*, 0, SystemAllocPolicy> parseWorklist, parseFinishedList;
|
|
|
|
/* Worklist for source compression worker threads. */
|
|
Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
|
|
|
|
WorkerThreadState() { mozilla::PodZero(this); }
|
|
~WorkerThreadState();
|
|
|
|
bool init(JSRuntime *rt);
|
|
void cleanup(JSRuntime *rt);
|
|
|
|
void lock();
|
|
void unlock();
|
|
|
|
# ifdef DEBUG
|
|
bool isLocked();
|
|
# endif
|
|
|
|
void wait(CondVar which, uint32_t timeoutMillis = 0);
|
|
void notifyAll(CondVar which);
|
|
|
|
bool canStartAsmJSCompile();
|
|
bool canStartIonCompile();
|
|
bool canStartParseTask();
|
|
bool canStartCompressionTask();
|
|
|
|
uint32_t harvestFailedAsmJSJobs() {
|
|
JS_ASSERT(isLocked());
|
|
uint32_t n = numAsmJSFailedJobs;
|
|
numAsmJSFailedJobs = 0;
|
|
return n;
|
|
}
|
|
void noteAsmJSFailure(void *func) {
|
|
// Be mindful to signal the main thread after calling this function.
|
|
JS_ASSERT(isLocked());
|
|
if (!asmJSFailedFunction)
|
|
asmJSFailedFunction = func;
|
|
numAsmJSFailedJobs++;
|
|
}
|
|
bool asmJSWorkerFailed() const {
|
|
return bool(numAsmJSFailedJobs);
|
|
}
|
|
void resetAsmJSFailureState() {
|
|
numAsmJSFailedJobs = 0;
|
|
asmJSFailedFunction = nullptr;
|
|
}
|
|
void *maybeAsmJSFailedFunction() const {
|
|
return asmJSFailedFunction;
|
|
}
|
|
|
|
JSScript *finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token);
|
|
bool compressionInProgress(SourceCompressionTask *task);
|
|
SourceCompressionTask *compressionTaskForSource(ScriptSource *ss);
|
|
|
|
private:
|
|
|
|
/*
|
|
* Lock protecting all mutable shared state accessed by helper threads, and
|
|
* used by all condition variables.
|
|
*/
|
|
PRLock *workerLock;
|
|
|
|
# ifdef DEBUG
|
|
PRThread *lockOwner;
|
|
# endif
|
|
|
|
/* Condvars for threads waiting/notifying each other. */
|
|
PRCondVar *consumerWakeup;
|
|
PRCondVar *producerWakeup;
|
|
|
|
/*
|
|
* Number of AsmJS workers that encountered failure for the active module.
|
|
* Their parent is logically the main thread, and this number serves for harvesting.
|
|
*/
|
|
uint32_t numAsmJSFailedJobs;
|
|
|
|
/*
|
|
* Function index |i| in |Module.function(i)| of first failed AsmJS function.
|
|
* -1 if no function has failed.
|
|
*/
|
|
void *asmJSFailedFunction;
|
|
};
|
|
|
|
/* Individual helper thread, one allocated per core. */
|
|
struct WorkerThread
|
|
{
|
|
JSRuntime *runtime;
|
|
|
|
mozilla::Maybe<PerThreadData> threadData;
|
|
PRThread *thread;
|
|
|
|
/* Indicate to an idle thread that it should finish executing. */
|
|
bool terminate;
|
|
|
|
/* Any builder currently being compiled by Ion on this thread. */
|
|
jit::IonBuilder *ionBuilder;
|
|
|
|
/* Any AsmJS data currently being optimized by Ion on this thread. */
|
|
AsmJSParallelTask *asmData;
|
|
|
|
/* Any source being parsed/emitted on this thread. */
|
|
ParseTask *parseTask;
|
|
|
|
/* Any source being compressed on this thread. */
|
|
SourceCompressionTask *compressionTask;
|
|
|
|
bool idle() const {
|
|
return !ionBuilder && !asmData && !parseTask && !compressionTask;
|
|
}
|
|
|
|
inline void maybePause();
|
|
|
|
void pause();
|
|
void destroy();
|
|
|
|
void handleAsmJSWorkload(WorkerThreadState &state);
|
|
void handleIonWorkload(WorkerThreadState &state);
|
|
void handleParseWorkload(WorkerThreadState &state);
|
|
void handleCompressionWorkload(WorkerThreadState &state);
|
|
|
|
static void ThreadMain(void *arg);
|
|
void threadLoop();
|
|
};
|
|
|
|
#endif /* JS_WORKER_THREADS */
|
|
|
|
inline bool
|
|
OffThreadIonCompilationEnabled(JSRuntime *rt)
|
|
{
|
|
#ifdef JS_WORKER_THREADS
|
|
return rt->useHelperThreads()
|
|
&& rt->helperThreadCount() != 0
|
|
&& rt->useHelperThreadsForIonCompilation();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/* Methods for interacting with worker threads. */
|
|
|
|
/* Initialize worker threads unless already initialized. */
|
|
bool
|
|
EnsureWorkerThreadsInitialized(ExclusiveContext *cx);
|
|
|
|
/* Perform MIR optimization and LIR generation on a single function. */
|
|
bool
|
|
StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData);
|
|
|
|
/*
|
|
* Schedule an Ion compilation for a script, given a builder which has been
|
|
* generated and read everything needed from the VM state.
|
|
*/
|
|
bool
|
|
StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder);
|
|
|
|
/*
|
|
* Cancel a scheduled or in progress Ion compilation for script. If script is
|
|
* nullptr, all compilations for the compartment are cancelled.
|
|
*/
|
|
void
|
|
CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
|
|
|
|
/*
|
|
* Start a parse/emit cycle for a stream of source. The characters must stay
|
|
* alive until the compilation finishes.
|
|
*/
|
|
bool
|
|
StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
|
|
const jschar *chars, size_t length, HandleObject scopeChain,
|
|
JS::OffThreadCompileCallback callback, void *callbackData);
|
|
|
|
/* Block until in progress and pending off thread parse jobs have finished. */
|
|
void
|
|
WaitForOffThreadParsingToFinish(JSRuntime *rt);
|
|
|
|
/* Start a compression job for the specified token. */
|
|
bool
|
|
StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
|
|
|
|
class AutoLockWorkerThreadState
|
|
{
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
#ifdef JS_WORKER_THREADS
|
|
WorkerThreadState &state;
|
|
|
|
public:
|
|
AutoLockWorkerThreadState(WorkerThreadState &state
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: state(state)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
state.lock();
|
|
}
|
|
|
|
~AutoLockWorkerThreadState() {
|
|
state.unlock();
|
|
}
|
|
#else
|
|
public:
|
|
AutoLockWorkerThreadState(WorkerThreadState &state
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
class AutoUnlockWorkerThreadState
|
|
{
|
|
JSRuntime *rt;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
public:
|
|
|
|
AutoUnlockWorkerThreadState(JSRuntime *rt
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: rt(rt)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
#ifdef JS_WORKER_THREADS
|
|
JS_ASSERT(rt->workerThreadState);
|
|
rt->workerThreadState->unlock();
|
|
#else
|
|
(void)this->rt;
|
|
#endif
|
|
}
|
|
|
|
~AutoUnlockWorkerThreadState()
|
|
{
|
|
#ifdef JS_WORKER_THREADS
|
|
rt->workerThreadState->lock();
|
|
#endif
|
|
}
|
|
};
|
|
|
|
#ifdef JS_WORKER_THREADS
|
|
|
|
inline void
|
|
WorkerThread::maybePause()
|
|
{
|
|
if (runtime->workerThreadState->shouldPause) {
|
|
AutoLockWorkerThreadState lock(*runtime->workerThreadState);
|
|
pause();
|
|
}
|
|
}
|
|
|
|
#endif // JS_WORKER_THREADS
|
|
|
|
/* Pause any threads that are running jobs off thread during GC activity. */
|
|
class AutoPauseWorkersForGC
|
|
{
|
|
#ifdef JS_WORKER_THREADS
|
|
JSRuntime *runtime;
|
|
bool needsUnpause;
|
|
mozilla::DebugOnly<bool> oldExclusiveThreadsPaused;
|
|
#endif
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
public:
|
|
AutoPauseWorkersForGC(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
|
~AutoPauseWorkersForGC();
|
|
};
|
|
|
|
/*
|
|
* If the current thread is a worker thread, treat it as paused during this
|
|
* class's lifetime. This should be used at any time the current thread is
|
|
* waiting for a worker to complete.
|
|
*/
|
|
class AutoPauseCurrentWorkerThread
|
|
{
|
|
#ifdef JS_WORKER_THREADS
|
|
ExclusiveContext *cx;
|
|
#endif
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
public:
|
|
AutoPauseCurrentWorkerThread(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
|
~AutoPauseCurrentWorkerThread();
|
|
};
|
|
|
|
/* Wait for any in progress off thread parses to halt. */
|
|
void
|
|
PauseOffThreadParsing();
|
|
|
|
/* Resume any paused off thread parses. */
|
|
void
|
|
ResumeOffThreadParsing();
|
|
|
|
#ifdef JS_ION
|
|
struct AsmJSParallelTask
|
|
{
|
|
LifoAlloc lifo; // Provider of all heap memory used for compilation.
|
|
void *func; // Really, a ModuleCompiler::Func*
|
|
jit::MIRGenerator *mir; // Passed from main thread to worker.
|
|
jit::LIRGraph *lir; // Passed from worker to main thread.
|
|
unsigned compileTime;
|
|
|
|
AsmJSParallelTask(size_t defaultChunkSize)
|
|
: lifo(defaultChunkSize), func(nullptr), mir(nullptr), lir(nullptr), compileTime(0)
|
|
{ }
|
|
|
|
void init(void *func, jit::MIRGenerator *mir) {
|
|
this->func = func;
|
|
this->mir = mir;
|
|
this->lir = nullptr;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
struct ParseTask
|
|
{
|
|
ExclusiveContext *cx;
|
|
CompileOptions options;
|
|
const jschar *chars;
|
|
size_t length;
|
|
LifoAlloc alloc;
|
|
|
|
// Rooted pointer to the scope in the target compartment which the
|
|
// resulting script will be merged into. This is not safe to use off the
|
|
// main thread.
|
|
JSObject *scopeChain;
|
|
|
|
// Callback invoked off the main thread when the parse finishes.
|
|
JS::OffThreadCompileCallback callback;
|
|
void *callbackData;
|
|
|
|
// Holds the final script between the invocation of the callback and the
|
|
// point where FinishOffThreadScript is called, which will destroy the
|
|
// ParseTask.
|
|
JSScript *script;
|
|
|
|
// Any errors or warnings produced during compilation. These are reported
|
|
// when finishing the script.
|
|
Vector<frontend::CompileError *> errors;
|
|
|
|
ParseTask(ExclusiveContext *cx, const CompileOptions &options,
|
|
const jschar *chars, size_t length, JSObject *scopeChain,
|
|
JS::OffThreadCompileCallback callback, void *callbackData);
|
|
|
|
~ParseTask();
|
|
};
|
|
|
|
// Compression tasks are allocated on the stack by their triggering thread,
|
|
// which will block on the compression completing as the task goes out of scope
|
|
// to ensure it completes at the required time.
|
|
struct SourceCompressionTask
|
|
{
|
|
friend class ScriptSource;
|
|
|
|
#ifdef JS_WORKER_THREADS
|
|
// Thread performing the compression.
|
|
WorkerThread *workerThread;
|
|
#endif
|
|
|
|
private:
|
|
// Context from the triggering thread. Don't use this off thread!
|
|
ExclusiveContext *cx;
|
|
|
|
ScriptSource *ss;
|
|
const jschar *chars;
|
|
bool oom;
|
|
|
|
// Atomic flag to indicate to a worker thread that it should abort
|
|
// compression on the source.
|
|
#ifdef JS_THREADSAFE
|
|
mozilla::Atomic<int32_t, mozilla::Relaxed> abort_;
|
|
#else
|
|
int32_t abort_;
|
|
#endif
|
|
|
|
public:
|
|
explicit SourceCompressionTask(ExclusiveContext *cx)
|
|
: cx(cx), ss(nullptr), chars(nullptr), oom(false), abort_(0)
|
|
{
|
|
#ifdef JS_WORKER_THREADS
|
|
workerThread = nullptr;
|
|
#endif
|
|
}
|
|
|
|
~SourceCompressionTask()
|
|
{
|
|
complete();
|
|
}
|
|
|
|
void maybePause() {
|
|
#ifdef JS_WORKER_THREADS
|
|
workerThread->maybePause();
|
|
#endif
|
|
}
|
|
|
|
bool compress();
|
|
bool complete();
|
|
void abort() { abort_ = 1; }
|
|
bool active() const { return !!ss; }
|
|
ScriptSource *source() { return ss; }
|
|
const jschar *uncompressedChars() { return chars; }
|
|
void setOOM() { oom = true; }
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
#endif /* jsworkers_h */
|