Bug 1387211 Avoid potential deadlock during worker shutdown. r=billm
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6216,30 +6216,44 @@ WorkerPrivate::NotifyInternal(JSContext*
Status previousStatus;
{
MutexAutoLock lock(mMutex);
if (mStatus >= aStatus) {
return true;
}
+ // Make sure the hybrid event target stops dispatching runnables
+ // once we reaching the killing state.
+ if (aStatus == Killing) {
+ // To avoid deadlock we always acquire the event target mutex before the
+ // worker private mutex. (We do it in this order because this is what
+ // workers best for event dispatching.) To enforce that order here we
+ // need to unlock the worker private mutex before we lock the event target
+ // mutex in ForgetWorkerPrivate.
+ {
+ MutexAutoUnlock unlock(mMutex);
+ mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
+ }
+
+ // Check the status code again in case another NotifyInternal came in
+ // while we were unlocked above.
+ if (mStatus >= aStatus) {
+ return true;
+ }
+ }
+
previousStatus = mStatus;
mStatus = aStatus;
// Mark parent status as closing immediately to avoid new events being
// dispatched after we clear the queue below.
if (aStatus == Closing) {
Close();
}
-
- // Make sure the hybrid event target stops dispatching runnables
- // once we reaching the killing state.
- if (aStatus == Killing) {
- mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
- }
}
if (mCrossThreadDispatcher) {
// Since we'll no longer process events, make sure we no longer allow
// anyone to post them. We have to do this without mMutex held, since our
// mutex must be acquired *after* mCrossThreadDispatcher's mutex when
// they're both held.
mCrossThreadDispatcher->Forget();