Bug 1503639 Part 4 - Allow all threads to diverge from the recording when repainting, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 31 Oct 2018 10:20:15 -1000
changeset 445253 b7b13712a7528ca745753adf67e8c8dbe2622b68
parent 445252 9d56cb4ea09e3ddd72722f835636227e4a5705c9
child 445254 f3a5ebd83214692da16e1188150c68d72505bf11
push id35014
push userdvarga@mozilla.com
push dateFri, 09 Nov 2018 10:01:40 +0000
treeherdermozilla-central@5e7636ec12c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1503639
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1503639 Part 4 - Allow all threads to diverge from the recording when repainting, r=mccr8.
toolkit/recordreplay/MiddlemanCall.cpp
toolkit/recordreplay/ipc/ChildIPC.cpp
toolkit/recordreplay/ipc/ChildInternal.h
--- a/toolkit/recordreplay/MiddlemanCall.cpp
+++ b/toolkit/recordreplay/MiddlemanCall.cpp
@@ -69,18 +69,17 @@ GatherDependentCalls(InfallibleVector<Mi
 bool
 SendCallToMiddleman(size_t aCallId, CallArguments* aArguments, bool aDiverged)
 {
   MOZ_RELEASE_ASSERT(IsReplaying());
 
   const Redirection& redirection = gRedirections[aCallId];
   MOZ_RELEASE_ASSERT(redirection.mMiddlemanCall);
 
-  Maybe<MonitorAutoLock> lock;
-  lock.emplace(*gMonitor);
+  MonitorAutoLock lock(*gMonitor);
 
   // Allocate and fill in a new MiddlemanCall.
   size_t id = gMiddlemanCalls.length();
   MiddlemanCall* newCall = new MiddlemanCall();
   gMiddlemanCalls.emplaceBack(newCall);
   newCall->mId = id;
   newCall->mCallId = aCallId;
   newCall->mArguments.CopyFrom(aArguments);
@@ -115,22 +114,17 @@ SendCallToMiddleman(size_t aCallId, Call
   InfallibleVector<char> inputData;
   BufferStream inputStream(&inputData);
   for (MiddlemanCall* call : outgoingCalls) {
     call->EncodeInput(inputStream);
   }
 
   // Perform the calls synchronously in the middleman.
   InfallibleVector<char> outputData;
-  if (!child::SendMiddlemanCallRequest(inputData.begin(), inputData.length(), &outputData)) {
-    // This thread is not allowed to perform middleman calls anymore. Release
-    // the lock and block until the process rewinds.
-    lock.reset();
-    Thread::WaitForever();
-  }
+  child::SendMiddlemanCallRequest(inputData.begin(), inputData.length(), &outputData);
 
   // Decode outputs for the calls just sent, and perform the ReplayOutput phase
   // on any older dependent calls we sent.
   BufferStream outputStream(outputData.begin(), outputData.length());
   for (MiddlemanCall* call : outgoingCalls) {
     call->DecodeOutput(outputStream);
 
     if (call != newCall) {
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -65,16 +65,20 @@ static FileHandle gCheckpointReadFd;
 static IntroductionMessage* gIntroductionMessage;
 
 // When recording, whether developer tools server code runs in the middleman.
 static bool gDebuggerRunsInMiddleman;
 
 // Any response received to the last MiddlemanCallRequest message.
 static MiddlemanCallResponseMessage* gCallResponseMessage;
 
+// Whether some thread has sent a MiddlemanCallRequest and is waiting for
+// gCallResponseMessage to be filled in.
+static bool gWaitingForCallResponse;
+
 // Processing routine for incoming channel messages.
 static void
 ChannelMessageHandler(Message* aMsg)
 {
   MOZ_RELEASE_ASSERT(MainThreadShouldPause() || aMsg->CanBeSentWhileUnpaused());
 
   switch (aMsg->mType) {
   case MessageType::Introduction: {
@@ -163,16 +167,17 @@ ChannelMessageHandler(Message* aMsg)
     const RunToPointMessage& nmsg = (const RunToPointMessage&) *aMsg;
     PauseMainThreadAndInvokeCallback([=]() {
         navigation::RunToPoint(nmsg.mTarget);
       });
     break;
   }
   case MessageType::MiddlemanCallResponse: {
     MonitorAutoLock lock(*gMonitor);
+    MOZ_RELEASE_ASSERT(gWaitingForCallResponse);
     MOZ_RELEASE_ASSERT(!gCallResponseMessage);
     gCallResponseMessage = (MiddlemanCallResponseMessage*) aMsg;
     aMsg = nullptr; // Avoid freeing the message below.
     gMonitor->NotifyAll();
     break;
   }
   default:
     MOZ_CRASH();
@@ -559,70 +564,71 @@ NotifyPaintComplete()
       gMonitor->Notify();
     }
   }
 
   // Notify the middleman about the completed paint from the main thread.
   NS_DispatchToMainThread(NewRunnableFunction("PaintFromMainThread", PaintFromMainThread));
 }
 
+// Whether we have repainted since diverging from the recording.
+static bool gDidRepaint;
+
 void
 Repaint(size_t* aWidth, size_t* aHeight)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(HasDivergedFromRecording());
 
   // Don't try to repaint if the first normal paint hasn't occurred yet.
   if (!gCompositorThreadId) {
     *aWidth = 0;
     *aHeight = 0;
     return;
   }
 
-  // Ignore the request to repaint if the compositor thread has already
-  // diverged from the recording. In this case we have already done a repaint
-  // and the last graphics we sent will still be correct.
-  Thread* compositorThread = Thread::GetById(gCompositorThreadId);
-  if (!compositorThread->WillDivergeFromRecordingSoon()) {
-    // Allow the compositor to diverge from the recording so it can perform
-    // any paint we are about to trigger, or finish any in flight paint that
+  // Ignore the request to repaint if we already triggered a repaint, in which
+  // case the last graphics we sent will still be correct.
+  if (!gDidRepaint) {
+    gDidRepaint = true;
+
+    // Allow other threads to diverge from the recording so the compositor can
+    // perform any paint we are about to trigger, or finish any in flight paint
     // that existed at the point we are paused at.
-    Thread::GetById(gCompositorThreadId)->SetShouldDivergeFromRecording();
-    Thread::ResumeSingleIdleThread(gCompositorThreadId);
+    for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
+      Thread::GetById(i)->SetShouldDivergeFromRecording();
+    }
+    Thread::ResumeIdleThreads();
 
     // Create an artifical vsync to see if graphics have changed since the last
     // paint and a new paint is needed.
     NotifyVsyncObserver();
 
     // Wait for the compositor to finish all in flight paints, including any
     // one we just triggered.
-    MonitorAutoLock lock(*gMonitor);
-    while (gNumPendingPaints) {
-      gMonitor->Wait();
+    {
+      MonitorAutoLock lock(*gMonitor);
+      while (gNumPendingPaints) {
+        gMonitor->Wait();
+      }
     }
+
+    Thread::WaitForIdleThreads();
   }
 
   if (gDrawTargetBuffer) {
     memcpy(gGraphicsShmem, gDrawTargetBuffer, gDrawTargetBufferSize);
     *aWidth = gPaintWidth;
     *aHeight = gPaintHeight;
   } else {
     *aWidth = 0;
     *aHeight = 0;
   }
 }
 
-static bool
-CompositorCanPerformMiddlemanCalls()
-{
-  // After repainting finishes the compositor is not allowed to send call
-  // requests to the middleman anymore.
-  return !!gNumPendingPaints;
-}
-
 bool
 SuppressMessageAfterDiverge(IPC::Message* aMsg)
 {
   MOZ_RELEASE_ASSERT(HasDivergedFromRecording());
 
   // Only messages necessary for compositing can be sent after the sending
   // thread has diverged from the recording. Sending other messages can risk
   // deadlocking when a necessary lock is held by an idle thread (we probably
@@ -705,53 +711,43 @@ HitBreakpoint(bool aRecordingEndpoint, c
   HitBreakpointMessage* msg =
     HitBreakpointMessage::New(aRecordingEndpoint, aBreakpoints, aNumBreakpoints);
   PauseMainThreadAndInvokeCallback([=]() {
       gChannel->SendMessage(*msg);
       free(msg);
     });
 }
 
-bool
+void
 SendMiddlemanCallRequest(const char* aInputData, size_t aInputSize,
                          InfallibleVector<char>* aOutputData)
 {
-  Thread* thread = Thread::Current();
-
-  // Middleman calls can only be made from the main and compositor threads.
-  // These two threads cannot simultaneously send call requests or other
-  // messages, as doing so will race both here and in Channel::SendMessage.
-  // CompositorCanPerformMiddlemanCalls() ensures that the main thread is
-  // not actively sending messages at times when the compositor performs
-  // middleman calls.
-  MOZ_RELEASE_ASSERT(thread->IsMainThread() || thread->Id() == gCompositorThreadId);
-
-  if (thread->Id() == gCompositorThreadId && !CompositorCanPerformMiddlemanCalls()) {
-    return false;
-  }
-
+  AutoPassThroughThreadEvents pt;
   MonitorAutoLock lock(*gMonitor);
 
-  MOZ_RELEASE_ASSERT(!gCallResponseMessage);
+  while (gWaitingForCallResponse) {
+    gMonitor->Wait();
+  }
+  gWaitingForCallResponse = true;
 
   MiddlemanCallRequestMessage* msg = MiddlemanCallRequestMessage::New(aInputData, aInputSize);
   gChannel->SendMessage(*msg);
   free(msg);
 
   while (!gCallResponseMessage) {
     gMonitor->Wait();
   }
 
   aOutputData->append(gCallResponseMessage->BinaryData(), gCallResponseMessage->BinaryDataSize());
 
   free(gCallResponseMessage);
   gCallResponseMessage = nullptr;
+  gWaitingForCallResponse = false;
 
   gMonitor->Notify();
-  return true;
 }
 
 void
 SendResetMiddlemanCalls()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   gChannel->SendMessage(ResetMiddlemanCallsMessage());
 }
--- a/toolkit/recordreplay/ipc/ChildInternal.h
+++ b/toolkit/recordreplay/ipc/ChildInternal.h
@@ -121,17 +121,17 @@ void ReportFatalError(const char* aForma
 // Mark a time span when the main thread is idle.
 void BeginIdleTime();
 void EndIdleTime();
 
 // Whether the middleman runs developer tools server code.
 bool DebuggerRunsInMiddleman();
 
 // Send messages operating on middleman calls.
-bool SendMiddlemanCallRequest(const char* aInputData, size_t aInputSize,
+void SendMiddlemanCallRequest(const char* aInputData, size_t aInputSize,
                               InfallibleVector<char>* aOutputData);
 void SendResetMiddlemanCalls();
 
 } // namespace child
 
 } // namespace recordreplay
 } // namespace mozilla