зеркало из https://github.com/mozilla/gecko-dev.git
Bug 916753, Bug 916531, Bug 916504 - Fix various deadlocks, r=billm,jandem.
This commit is contained in:
Родитель
3cdaadd0f1
Коммит
aef42563a1
|
@ -5015,13 +5015,14 @@ static AsmJSParallelTask *
|
|||
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
||||
{
|
||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
||||
AutoPauseCurrentWorkerThread maybePause(m.cx());
|
||||
|
||||
while (!group.state.asmJSWorkerFailed()) {
|
||||
if (!group.state.asmJSFinishedList.empty()) {
|
||||
group.outstandingJobs--;
|
||||
return group.state.asmJSFinishedList.popCopy();
|
||||
}
|
||||
group.state.wait(WorkerThreadState::MAIN);
|
||||
group.state.wait(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -5139,10 +5140,12 @@ CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
|
|||
// Eliminate tasks that failed without adding to the finished list.
|
||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
||||
|
||||
AutoPauseCurrentWorkerThread maybePause(m.cx());
|
||||
|
||||
// Any remaining tasks are therefore undergoing active compilation.
|
||||
JS_ASSERT(group.outstandingJobs >= 0);
|
||||
while (group.outstandingJobs > 0) {
|
||||
group.state.wait(WorkerThreadState::MAIN);
|
||||
group.state.wait(WorkerThreadState::CONSUMER);
|
||||
|
||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
||||
group.outstandingJobs -= group.state.asmJSFinishedList.length();
|
||||
|
|
|
@ -1934,6 +1934,10 @@ js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Don't trigger GCs when allocating under the operation callback lock. */
|
||||
if (rt->currentThreadOwnsOperationCallbackLock())
|
||||
return;
|
||||
|
||||
JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
|
||||
if (rt->isHeapBusy())
|
||||
|
@ -1957,6 +1961,10 @@ js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
|
|||
|
||||
JSRuntime *rt = zone->runtimeFromMainThread();
|
||||
|
||||
/* Don't trigger GCs when allocating under the operation callback lock. */
|
||||
if (rt->currentThreadOwnsOperationCallbackLock())
|
||||
return;
|
||||
|
||||
if (rt->isHeapBusy())
|
||||
return;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
|
|||
if (!state.asmJSWorklist.append(asmData))
|
||||
return false;
|
||||
|
||||
state.notify(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
|
|||
if (!state.ionWorklist.append(builder))
|
||||
return false;
|
||||
|
||||
state.notify(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
|||
CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
|
||||
{
|
||||
helper.ionBuilder->cancel();
|
||||
state.wait(WorkerThreadState::MAIN);
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,7 +284,7 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
|
|||
|
||||
task.forget();
|
||||
|
||||
state.notify(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -306,7 +306,7 @@ js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
|
|||
if (!parseInProgress)
|
||||
break;
|
||||
}
|
||||
state.wait(WorkerThreadState::MAIN);
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,12 +322,12 @@ WorkerThreadState::init(JSRuntime *rt)
|
|||
if (!workerLock)
|
||||
return false;
|
||||
|
||||
mainWakeup = PR_NewCondVar(workerLock);
|
||||
if (!mainWakeup)
|
||||
consumerWakeup = PR_NewCondVar(workerLock);
|
||||
if (!consumerWakeup)
|
||||
return false;
|
||||
|
||||
helperWakeup = PR_NewCondVar(workerLock);
|
||||
if (!helperWakeup)
|
||||
producerWakeup = PR_NewCondVar(workerLock);
|
||||
if (!producerWakeup)
|
||||
return false;
|
||||
|
||||
numThreads = rt->helperThreadCount();
|
||||
|
@ -388,11 +388,11 @@ WorkerThreadState::~WorkerThreadState()
|
|||
if (workerLock)
|
||||
PR_DestroyLock(workerLock);
|
||||
|
||||
if (mainWakeup)
|
||||
PR_DestroyCondVar(mainWakeup);
|
||||
if (consumerWakeup)
|
||||
PR_DestroyCondVar(consumerWakeup);
|
||||
|
||||
if (helperWakeup)
|
||||
PR_DestroyCondVar(helperWakeup);
|
||||
if (producerWakeup)
|
||||
PR_DestroyCondVar(producerWakeup);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -431,7 +431,7 @@ WorkerThreadState::wait(CondVar which, uint32_t millis)
|
|||
lockOwner = NULL;
|
||||
#endif
|
||||
DebugOnly<PRStatus> status =
|
||||
PR_WaitCondVar((which == MAIN) ? mainWakeup : helperWakeup,
|
||||
PR_WaitCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup,
|
||||
millis ? PR_MillisecondsToInterval(millis) : PR_INTERVAL_NO_TIMEOUT);
|
||||
JS_ASSERT(status == PR_SUCCESS);
|
||||
#ifdef DEBUG
|
||||
|
@ -439,18 +439,11 @@ WorkerThreadState::wait(CondVar which, uint32_t millis)
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::notify(CondVar which)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
PR_NotifyCondVar((which == MAIN) ? mainWakeup : helperWakeup);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::notifyAll(CondVar which)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
PR_NotifyAllCondVar((which == MAIN) ? mainWakeup : helperWakeup);
|
||||
PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -613,7 +606,7 @@ WorkerThread::destroy()
|
|||
terminate = true;
|
||||
|
||||
/* Notify all workers, to ensure that this thread wakes up. */
|
||||
state.notifyAll(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
PR_JoinThread(thread);
|
||||
|
@ -667,7 +660,7 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
|||
if (!success) {
|
||||
asmData = NULL;
|
||||
state.noteAsmJSFailure(asmData->func);
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -676,7 +669,7 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
|||
asmData = NULL;
|
||||
|
||||
// Notify the main thread in case it's blocked waiting for a LifoAlloc.
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -709,7 +702,7 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
|||
ionBuilder = NULL;
|
||||
|
||||
// Notify the main thread in case it is waiting for the compilation to finish.
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
|
||||
// Ping the main thread so that the compiled code can be incorporated
|
||||
// at the next operation callback. Don't interrupt Ion code for this, as
|
||||
|
@ -764,7 +757,7 @@ WorkerThread::handleParseWorkload(WorkerThreadState &state)
|
|||
parseTask = NULL;
|
||||
|
||||
// Notify the main thread in case it is waiting for the parse/emit to finish.
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -787,7 +780,7 @@ WorkerThread::handleCompressionWorkload(WorkerThreadState &state)
|
|||
compressionTask = NULL;
|
||||
|
||||
// Notify the main thread in case it is waiting for the compression to finish.
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -802,7 +795,7 @@ js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
|||
if (!state.compressionWorklist.append(task))
|
||||
return false;
|
||||
|
||||
state.notify(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -829,23 +822,10 @@ SourceCompressionTask::complete()
|
|||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
|
||||
// If this compression task is itself being completed on a worker
|
||||
// thread, treat the thread as paused while waiting for the completion.
|
||||
// Otherwise we will not wake up and mark this as paused due to the
|
||||
// loop in AutoPauseWorkersForGC.
|
||||
if (cx->workerThread()) {
|
||||
state.numPaused++;
|
||||
if (state.numPaused == state.numThreads)
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
}
|
||||
|
||||
while (state.compressionInProgress(this))
|
||||
state.wait(WorkerThreadState::MAIN);
|
||||
|
||||
if (cx->workerThread()) {
|
||||
state.numPaused--;
|
||||
if (state.shouldPause)
|
||||
cx->workerThread()->pause();
|
||||
{
|
||||
AutoPauseCurrentWorkerThread maybePause(cx);
|
||||
while (state.compressionInProgress(this))
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
ss->ready_ = true;
|
||||
|
@ -930,7 +910,7 @@ WorkerThread::threadLoop()
|
|||
{
|
||||
break;
|
||||
}
|
||||
state.wait(WorkerThreadState::WORKER);
|
||||
state.wait(WorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
// Dispatch tasks, prioritizing AsmJS work.
|
||||
|
@ -976,8 +956,8 @@ AutoPauseWorkersForGC::AutoPauseWorkersForGC(JSRuntime *rt MOZ_GUARD_OBJECT_NOTI
|
|||
state.shouldPause = 1;
|
||||
|
||||
while (state.numPaused != state.numThreads) {
|
||||
state.notifyAll(WorkerThreadState::WORKER);
|
||||
state.wait(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -994,7 +974,42 @@ AutoPauseWorkersForGC::~AutoPauseWorkersForGC()
|
|||
state.shouldPause = 0;
|
||||
|
||||
// Notify all workers, to ensure that each wakes up.
|
||||
state.notifyAll(WorkerThreadState::WORKER);
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
AutoPauseCurrentWorkerThread::AutoPauseCurrentWorkerThread(ExclusiveContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: cx(cx)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
||||
// If the current thread is a worker thread, treat it as paused while
|
||||
// the caller is waiting for another worker thread to complete. Otherwise
|
||||
// we will not wake up and mark this as paused due to the loop in
|
||||
// AutoPauseWorkersForGC.
|
||||
if (cx->workerThread()) {
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
JS_ASSERT(state.isLocked());
|
||||
|
||||
state.numPaused++;
|
||||
if (state.numPaused == state.numThreads)
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
}
|
||||
}
|
||||
|
||||
AutoPauseCurrentWorkerThread::~AutoPauseCurrentWorkerThread()
|
||||
{
|
||||
if (cx->workerThread()) {
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
JS_ASSERT(state.isLocked());
|
||||
|
||||
state.numPaused--;
|
||||
|
||||
// Before resuming execution of the worker thread, make sure the main
|
||||
// thread does not expect worker threads to be paused.
|
||||
if (state.shouldPause)
|
||||
cx->workerThread()->pause();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1009,10 +1024,10 @@ WorkerThread::pause()
|
|||
|
||||
// Don't bother to notify the main thread until all workers have paused.
|
||||
if (state.numPaused == state.numThreads)
|
||||
state.notify(WorkerThreadState::MAIN);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
|
||||
while (state.shouldPause)
|
||||
state.wait(WorkerThreadState::WORKER);
|
||||
state.wait(WorkerThreadState::PRODUCER);
|
||||
|
||||
state.numPaused--;
|
||||
}
|
||||
|
@ -1080,6 +1095,16 @@ AutoPauseWorkersForGC::~AutoPauseWorkersForGC()
|
|||
{
|
||||
}
|
||||
|
||||
AutoPauseCurrentWorkerThread::AutoPauseCurrentWorkerThread(ExclusiveContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
AutoPauseCurrentWorkerThread::~AutoPauseCurrentWorkerThread()
|
||||
{
|
||||
}
|
||||
|
||||
frontend::CompileError &
|
||||
ExclusiveContext::addPendingCompileError()
|
||||
{
|
||||
|
|
|
@ -51,8 +51,11 @@ class WorkerThreadState
|
|||
uint32_t numPaused;
|
||||
|
||||
enum CondVar {
|
||||
MAIN,
|
||||
WORKER
|
||||
/* 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. */
|
||||
|
@ -88,7 +91,6 @@ class WorkerThreadState
|
|||
# endif
|
||||
|
||||
void wait(CondVar which, uint32_t timeoutMillis = 0);
|
||||
void notify(CondVar which);
|
||||
void notifyAll(CondVar which);
|
||||
|
||||
bool canStartAsmJSCompile();
|
||||
|
@ -136,11 +138,9 @@ class WorkerThreadState
|
|||
PRThread *lockOwner;
|
||||
# endif
|
||||
|
||||
/* Condvar to notify the main thread that work has been completed. */
|
||||
PRCondVar *mainWakeup;
|
||||
|
||||
/* Condvar to notify helper threads that they may be able to make progress. */
|
||||
PRCondVar *helperWakeup;
|
||||
/* Condvars for threads waiting/notifying each other. */
|
||||
PRCondVar *consumerWakeup;
|
||||
PRCondVar *producerWakeup;
|
||||
|
||||
/*
|
||||
* Number of AsmJS workers that encountered failure for the active module.
|
||||
|
@ -336,6 +336,23 @@ class AutoPauseWorkersForGC
|
|||
~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();
|
||||
|
|
|
@ -110,10 +110,8 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||
operationCallback(NULL),
|
||||
#ifdef JS_THREADSAFE
|
||||
operationCallbackLock(NULL),
|
||||
#ifdef DEBUG
|
||||
operationCallbackOwner(NULL),
|
||||
#endif
|
||||
#endif
|
||||
#ifdef JS_WORKER_THREADS
|
||||
workerThreadState(NULL),
|
||||
exclusiveAccessLock(NULL),
|
||||
|
|
|
@ -720,9 +720,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
* Protects all data that is touched in this process.
|
||||
*/
|
||||
PRLock *operationCallbackLock;
|
||||
#ifdef DEBUG
|
||||
PRThread *operationCallbackOwner;
|
||||
#endif
|
||||
public:
|
||||
#endif // JS_THREADSAFE
|
||||
|
||||
|
@ -732,16 +730,13 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
public:
|
||||
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
JS_ASSERT(!rt->currentThreadOwnsOperationCallbackLock());
|
||||
PR_Lock(rt->operationCallbackLock);
|
||||
#ifdef DEBUG
|
||||
rt->operationCallbackOwner = PR_GetCurrentThread();
|
||||
#endif
|
||||
}
|
||||
~AutoLockForOperationCallback() {
|
||||
JS_ASSERT(rt->operationCallbackOwner == PR_GetCurrentThread());
|
||||
#ifdef DEBUG
|
||||
rt->operationCallbackOwner = NULL;
|
||||
#endif
|
||||
PR_Unlock(rt->operationCallbackLock);
|
||||
}
|
||||
#else // JS_THREADSAFE
|
||||
|
@ -755,7 +750,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
};
|
||||
|
||||
bool currentThreadOwnsOperationCallbackLock() {
|
||||
#if defined(JS_THREADSAFE) && defined(DEBUG)
|
||||
#if defined(JS_THREADSAFE)
|
||||
return operationCallbackOwner == PR_GetCurrentThread();
|
||||
#else
|
||||
return true;
|
||||
|
|
Загрузка…
Ссылка в новой задаче