Bug 1500805 Part 1 - Allow idle threads to be selectively resumed, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 21 Oct 2018 15:01:54 -0600
changeset 491286 25e1e5dcacb2d8a968167ba7e3775e1fcd862590
parent 491285 101430a1be58500a099200dcc097ca95bbfa1bf8
child 491287 20691f6e5da854dabedc2c2beb97133599cec868
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersmccr8
bugs1500805
milestone65.0a1
Bug 1500805 Part 1 - Allow idle threads to be selectively resumed, r=mccr8.
toolkit/recordreplay/Thread.cpp
toolkit/recordreplay/Thread.h
--- a/toolkit/recordreplay/Thread.cpp
+++ b/toolkit/recordreplay/Thread.cpp
@@ -359,34 +359,26 @@ RecordReplayInterface_InternalAreThreadE
 }
 
 } // extern "C"
 
 ///////////////////////////////////////////////////////////////////////////////
 // Thread Coordination
 ///////////////////////////////////////////////////////////////////////////////
 
-// Whether all threads should attempt to idle.
-static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsShouldIdle;
-
-// Whether all threads are considered to be idle.
-static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsAreIdle;
-
 /* static */ void
 Thread::WaitForIdleThreads()
 {
   MOZ_RELEASE_ASSERT(CurrentIsMainThread());
 
-  MOZ_RELEASE_ASSERT(!gThreadsShouldIdle);
-  MOZ_RELEASE_ASSERT(!gThreadsAreIdle);
-  gThreadsShouldIdle = true;
-
   MonitorAutoLock lock(*gMonitor);
   for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
-    GetById(i)->mUnrecordedWaitNotified = false;
+    Thread* thread = GetById(i);
+    thread->mShouldIdle = true;
+    thread->mUnrecordedWaitNotified = false;
   }
   while (true) {
     bool done = true;
     for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
       Thread* thread = GetById(i);
       if (!thread->mIdle) {
         done = false;
 
@@ -421,33 +413,31 @@ Thread::WaitForIdleThreads()
       }
     }
     if (done) {
       break;
     }
     MonitorAutoUnlock unlock(*gMonitor);
     WaitNoIdle();
   }
+}
 
-  gThreadsAreIdle = true;
+/* static */ void
+Thread::ResumeSingleIdleThread(size_t aId)
+{
+  GetById(aId)->mShouldIdle = false;
+  Notify(aId);
 }
 
 /* static */ void
 Thread::ResumeIdleThreads()
 {
   MOZ_RELEASE_ASSERT(CurrentIsMainThread());
-
-  MOZ_RELEASE_ASSERT(gThreadsAreIdle);
-  gThreadsAreIdle = false;
-
-  MOZ_RELEASE_ASSERT(gThreadsShouldIdle);
-  gThreadsShouldIdle = false;
-
   for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
-    Notify(i);
+    ResumeSingleIdleThread(i);
   }
 }
 
 void
 Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback, bool aOnlyWhenDiverged)
 {
   MonitorAutoLock lock(*gMonitor);
   if (mUnrecordedWaitCallback) {
@@ -459,26 +449,27 @@ Thread::NotifyUnrecordedWait(const std::
     MOZ_RELEASE_ASSERT(!mUnrecordedWaitNotified);
   }
 
   mUnrecordedWaitCallback = aCallback;
   mUnrecordedWaitOnlyWhenDiverged = aOnlyWhenDiverged;
 
   // The main thread might be able to make progress now by calling the routine
   // if it is waiting for idle replay threads.
-  if (gThreadsShouldIdle) {
+  if (mShouldIdle) {
     Notify(MainThreadId);
   }
 }
 
 /* static */ void
 Thread::MaybeWaitForCheckpointSave()
 {
   MonitorAutoLock lock(*gMonitor);
-  while (gThreadsShouldIdle) {
+  Thread* thread = Thread::Current();
+  while (thread->mShouldIdle) {
     MonitorAutoUnlock unlock(*gMonitor);
     Wait();
   }
 }
 
 extern "C" {
 
 MOZ_EXPORT void
@@ -526,32 +517,32 @@ Thread::Wait()
   int stackSeparator = 0;
   if (!SaveThreadState(thread->Id(), &stackSeparator)) {
     // We just restored a checkpoint, notify the main thread since it is waiting
     // for all threads to restore their stacks.
     Notify(MainThreadId);
   }
 
   thread->mIdle = true;
-  if (gThreadsShouldIdle) {
+  if (thread->mShouldIdle) {
     // Notify the main thread that we just became idle.
     Notify(MainThreadId);
   }
 
   do {
     // Do the actual waiting for another thread to notify this one.
     WaitNoIdle();
 
     // Rewind this thread if the main thread told us to do so. The main
     // thread is responsible for rewinding its own stack.
     if (ShouldRestoreThreadStack(thread->Id())) {
       RestoreThreadStack(thread->Id());
       Unreachable();
     }
-  } while (gThreadsShouldIdle);
+  } while (thread->mShouldIdle);
 
   thread->mIdle = false;
   thread->SetPassThrough(false);
 }
 
 /* static */ void
 Thread::WaitForever()
 {
--- a/toolkit/recordreplay/Thread.h
+++ b/toolkit/recordreplay/Thread.h
@@ -122,16 +122,19 @@ private:
   size_t mStackSize;
 
   // File descriptor to block on when the thread is idle, fixed at creation.
   FileHandle mIdlefd;
 
   // File descriptor to notify to wake the thread up, fixed at creation.
   FileHandle mNotifyfd;
 
+  // Whether the thread should attempt to idle.
+  Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mShouldIdle;
+
   // Whether the thread is waiting on idlefd.
   Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mIdle;
 
   // Any callback which should be invoked so the thread can make progress,
   // and whether the callback has been invoked yet while the main thread is
   // waiting for threads to become idle. Protected by the thread monitor.
   std::function<void()> mUnrecordedWaitCallback;
   bool mUnrecordedWaitOnlyWhenDiverged;
@@ -284,16 +287,23 @@ public:
 
   // Wait for all other threads to enter the idle state necessary for saving
   // or restoring a checkpoint. This may only be called on the main thread.
   static void WaitForIdleThreads();
 
   // After WaitForIdleThreads(), the main thread will call this to allow
   // other threads to resume execution.
   static void ResumeIdleThreads();
+
+  // Allow a single thread to resume execution.
+  static void ResumeSingleIdleThread(size_t aId);
+
+  // Return whether this thread is in the idle state entered after
+  // WaitForIdleThreads.
+  bool IsIdle() { return mIdle; }
 };
 
 // This uses a stack pointer instead of TLS to make sure events are passed
 // through, for avoiding thorny reentrance issues.
 class AutoEnsurePassThroughThreadEventsUseStackPointer
 {
   Thread* mThread;
   bool mPassedThrough;