Bug 1500805 Part 3 - Rewind instead of deadlocking when taking a lock held by an idle thread, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 21 Oct 2018 15:03:34 -0600
changeset 491288 ac631ceaa2b95d5c44f470cffba8bd1b339b0403
parent 491287 20691f6e5da854dabedc2c2beb97133599cec868
child 491289 9f6bb5ca3b3718a2e074077210fc60c6f5b9d0e8
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersmccr8
bugs1500805
milestone65.0a1
Bug 1500805 Part 3 - Rewind instead of deadlocking when taking a lock held by an idle thread, r=mccr8.
toolkit/recordreplay/Lock.cpp
toolkit/recordreplay/Lock.h
--- a/toolkit/recordreplay/Lock.cpp
+++ b/toolkit/recordreplay/Lock.cpp
@@ -130,20 +130,31 @@ Lock::Find(void* aNativeLock)
 
   if (gLocks) {
     LockMap::iterator iter = gLocks->find(aNativeLock);
     if (iter != gLocks->end()) {
       // Now that we know the lock is recorded, check whether thread events
       // should be generated right now. Doing things in this order avoids
       // reentrancy issues when initializing the thread-local state used by
       // these calls.
-      if (AreThreadEventsPassedThrough() || HasDivergedFromRecording()) {
+      Lock* lock = iter->second;
+      if (AreThreadEventsPassedThrough()) {
         return nullptr;
       }
-      return iter->second;
+      if (HasDivergedFromRecording()) {
+        // When diverged from the recording, don't allow uses of locks that are
+        // held by idling threads that have not diverged from the recording.
+        // This will cause the process to deadlock, so rewind instead.
+        if (lock->mOwner && Thread::GetById(lock->mOwner)->IsIdle()) {
+          EnsureNotDivergedFromRecording();
+          Unreachable();
+        }
+        return nullptr;
+      }
+      return lock;
     }
   }
 
   return nullptr;
 }
 
 void
 Lock::Enter()
@@ -166,24 +177,29 @@ Lock::Enter()
   if (IsRecording()) {
     acquires->mAcquires->WriteScalar(thread->Id());
   } else {
     // Wait until this thread is next in line to acquire the lock, or until it
     // has been instructed to diverge from the recording.
     while (thread->Id() != acquires->mNextOwner && !thread->MaybeDivergeFromRecording()) {
       Thread::Wait();
     }
+    if (!thread->HasDivergedFromRecording()) {
+      mOwner = thread->Id();
+    }
   }
 }
 
 void
 Lock::Exit()
 {
   Thread* thread = Thread::Current();
   if (IsReplaying() && !thread->HasDivergedFromRecording()) {
+    mOwner = 0;
+
     // Notify the next owner before releasing the lock.
     LockAcquires* acquires = gLockAcquires.Get(mId);
     acquires->ReadAndNotifyNextOwner(thread);
   }
 }
 
 struct AtomicLock : public detail::MutexImpl
 {
--- a/toolkit/recordreplay/Lock.h
+++ b/toolkit/recordreplay/Lock.h
@@ -25,19 +25,22 @@ namespace recordreplay {
 // which they originally occurred.
 
 // Information about a recorded lock.
 class Lock
 {
   // Unique ID for this lock.
   size_t mId;
 
+  // When replaying, any thread owning this lock as part of the recording.
+  Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> mOwner;
+
 public:
   explicit Lock(size_t aId)
-    : mId(aId)
+    : mId(aId), mOwner(0)
   {
     MOZ_ASSERT(aId);
   }
 
   size_t Id() { return mId; }
 
   // When recording, this is called after the lock has been acquired, and
   // records the acquire in the lock's acquire order stream. When replaying,