Bug 1540080 - Execute the canceling runnable after self.close() even when we have sync event loops, r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D25605

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrea Marchesini 2019-04-02 20:53:16 +00:00
Родитель f230fa362c
Коммит 01c18856e4
2 изменённых файлов: 48 добавлений и 21 удалений

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

@ -2063,8 +2063,8 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
mCreationTimeStamp(TimeStamp::Now()),
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
mWorkerThreadAccessible(aParent),
mPostSyncLoopOperations(0),
mParentWindowPaused(false),
mPendingEventQueueClearing(false),
mCancelAllPendingRunnables(false),
mWorkerScriptExecutedSuccessfully(false),
mFetchHandlerWasAdded(false),
@ -3160,7 +3160,7 @@ void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
MOZ_ASSERT(data->mChildWorkers.IsEmpty());
}
MOZ_ASSERT(mSyncLoopStack.IsEmpty());
MOZ_ASSERT(!mPendingEventQueueClearing);
MOZ_ASSERT(mPostSyncLoopOperations == 0);
ClearMainEventQueue(aRanOrNot);
#ifdef DEBUG
@ -3679,14 +3679,37 @@ bool WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex) {
static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
queue->PopEventQueue(nestedEventTarget);
if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
mPendingEventQueueClearing = false;
ClearMainEventQueue(WorkerRan);
if (mSyncLoopStack.IsEmpty()) {
if ((mPostSyncLoopOperations & ePendingEventQueueClearing)) {
ClearMainEventQueue(WorkerRan);
}
if ((mPostSyncLoopOperations & eDispatchCancelingRunnable)) {
DispatchCancelingRunnable();
}
mPostSyncLoopOperations = 0;
}
return result;
}
void WorkerPrivate::DispatchCancelingRunnable() {
// Here we use a normal runnable to know when the current JS chunk of code
// is finished. We cannot use a WorkerRunnable because they are not
// accepted any more by the worker, and we do not want to use a
// WorkerControlRunnable because they are immediately executed.
RefPtr<CancelingRunnable> r = new CancelingRunnable();
mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
// At the same time, we want to be sure that we interrupt infinite loops.
// The following runnable starts a timer that cancel the worker, from the
// parent thread, after CANCELING_TIMEOUT millseconds.
RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
new CancelingWithTimeoutOnParentRunnable(this);
rr->Dispatch();
}
void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget,
bool aResult) {
AssertIsOnWorkerThread();
@ -3971,7 +3994,7 @@ bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
// 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.IsEmpty()) {
mPendingEventQueueClearing = true;
mPostSyncLoopOperations |= ePendingEventQueueClearing;
} else {
ClearMainEventQueue(WorkerRan);
}
@ -3986,20 +4009,10 @@ bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
// Don't abort the script now, but we dispatch a runnable to do it when the
// current JS frame is executed.
if (aStatus == Closing) {
if (mSyncLoopStack.IsEmpty()) {
// Here we use a normal runnable to know when the current JS chunk of code
// is finished. We cannot use a WorkerRunnable because they are not
// accepted any more by the worker, and we do not want to use a
// WorkerControlRunnable because they are immediately executed.
RefPtr<CancelingRunnable> r = new CancelingRunnable();
mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
// At the same time, we want to be sure that we interrupt infinite loops.
// The following runnable starts a timer that cancel the worker, from the
// parent thread, after CANCELING_TIMEOUT millseconds.
RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
new CancelingWithTimeoutOnParentRunnable(this);
rr->Dispatch();
if (!mSyncLoopStack.IsEmpty()) {
mPostSyncLoopOperations |= eDispatchCancelingRunnable;
} else {
DispatchCancelingRunnable();
}
return true;
}

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

@ -925,6 +925,13 @@ class WorkerPrivate : public RelativeTimeline {
nsIEventTarget* aSyncLoopTarget,
const MutexAutoLock& aProofOfLock);
// This method dispatches a simple runnable that starts the shutdown procedure
// after a self.close(). This method is called after a ClearMainEventQueue()
// to be sure that the canceling runnable is the only one in the queue. We
// need this async operation to be sure that all the current JS code is
// executed.
void DispatchCancelingRunnable();
class EventTarget;
friend class EventTarget;
friend class mozilla::dom::WorkerHolder;
@ -1073,9 +1080,16 @@ class WorkerPrivate : public RelativeTimeline {
};
ThreadBound<WorkerThreadAccessible> mWorkerThreadAccessible;
uint32_t mPostSyncLoopOperations;
// List of operations to do at the end of the last sync event loop.
enum {
ePendingEventQueueClearing = 0x01,
eDispatchCancelingRunnable = 0x02,
};
bool mParentWindowPaused;
bool mPendingEventQueueClearing;
bool mCancelAllPendingRunnables;
bool mWorkerScriptExecutedSuccessfully;
bool mFetchHandlerWasAdded;