Bug 1208687: Only discard events from the outermost queue. r=ehsan

When workers shut down we discard the event queue rather than running it to completion.  Originally workers managed their event queue themselves and would simply iterate through the array of events and cancel them all.  After bug 914762 this was done by setting a (thread-)global "canceling" flag and then calling NS_ProcessPendingEvents.  But this neglects that a shut down request can be received while the worker is in a sync queue.  In this case, calling NS_ProcessPendingEvents will process any events pending in the sync queue, which is *not* the queue we need to cancel.

The fix is, if we are in a sync queue when NotifyInternal is called, to defer clearing the queue until the top-most sync queue is destroyed and we are about to return to the regular event queue.  Only then can we call NS_ProcessPendingEvents to clear out the queue.  Because we can never process any events from this queue while sync queues are active, the timing of the mass cancellation is unchanged from the perspective of events in the regular queue.
This commit is contained in:
Kyle Huey 2015-09-28 14:34:28 -07:00
Родитель b0aa2e52e9
Коммит ca2010bbb9
3 изменённых файлов: 30 добавлений и 8 удалений

Просмотреть файл

@ -575,6 +575,13 @@ private:
return true;
}
NS_IMETHOD Cancel() override
{
// We need to run regardless.
Run();
return WorkerRunnable::Cancel();
}
virtual void
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
override
@ -1230,6 +1237,13 @@ private:
return true;
}
NS_IMETHOD Cancel() override
{
// We need to run regardless.
Run();
return WorkerRunnable::Cancel();
}
};
class UpdateRuntimeOptionsRunnable final : public WorkerControlRunnable
@ -3823,6 +3837,7 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx,
, mRunningExpiredTimeouts(false)
, mCloseHandlerStarted(false)
, mCloseHandlerFinished(false)
, mPendingEventQueueClearing(false)
, mMemoryReporterRunning(false)
, mBlockedForMemoryReporter(false)
, mCancelAllPendingRunnables(false)
@ -4651,6 +4666,7 @@ WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
AssertIsOnWorkerThread();
MOZ_ASSERT(mChildWorkers.IsEmpty());
MOZ_ASSERT(mSyncLoopStack.IsEmpty());
MOZ_ASSERT(!mPendingEventQueueClearing);
ClearMainEventQueue(aRanOrNot);
#ifdef DEBUG
@ -4881,6 +4897,7 @@ WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
{
AssertIsOnWorkerThread();
MOZ_ASSERT(!mSyncLoopStack.Length());
MOZ_ASSERT(!mCancelAllPendingRunnables);
mCancelAllPendingRunnables = true;
@ -5243,6 +5260,11 @@ WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread)
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget)));
if (!mSyncLoopStack.Length() && mPendingEventQueueClearing) {
ClearMainEventQueue(WorkerRan);
mPendingEventQueueClearing = false;
}
return result;
}
@ -5509,7 +5531,13 @@ WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
// If this is the first time our status has changed then we need to clear the
// main event queue.
if (previousStatus == Running) {
ClearMainEventQueue(WorkerRan);
// NB: If we're in a sync loop, we can't clear the queue immediately,
// because this is the wrong queue. So we have to defer it until later.
if (mSyncLoopStack.Length()) {
mPendingEventQueueClearing = true;
} else {
ClearMainEventQueue(WorkerRan);
}
}
// If we've run the close handler, we don't need to do anything else.

Просмотреть файл

@ -944,6 +944,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
bool mRunningExpiredTimeouts;
bool mCloseHandlerStarted;
bool mCloseHandlerFinished;
bool mPendingEventQueueClearing;
bool mMemoryReporterRunning;
bool mBlockedForMemoryReporter;
bool mCancelAllPendingRunnables;

Просмотреть файл

@ -1,7 +0,0 @@
[incoming-message.html]
type: testharness
disabled:
if debug: unstable
[close() and incoming message]
expected: FAIL