Bug 1481346 - Fix problems when forwarding sync messages in the middleman, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 14 Aug 2018 00:30:09 +0000
changeset 486532 3352ce18917b2c7d210cc09aa1b610c2ca775580
parent 486531 d492e85d1040e12a5b34c25a45156e02d91d22fe
child 486533 290b508c04ee871a1582f4ede10ed1d6ed2cc8dd
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1481346
milestone63.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 1481346 - Fix problems when forwarding sync messages in the middleman, r=mccr8.
ipc/glue/MessageChannel.cpp
toolkit/recordreplay/ipc/ChildProcess.cpp
toolkit/recordreplay/ipc/ParentForwarding.cpp
toolkit/recordreplay/ipc/ParentIPC.cpp
toolkit/recordreplay/ipc/ParentInternal.h
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -2185,17 +2185,20 @@ void
 MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
 {
     AssertWorkerThread();
 
     mozilla::TimeStamp start = TimeStamp::Now();
 
     int nestedLevel = aMsg.nested_level();
 
-    MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread());
+    MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED ||
+                       NS_IsMainThread() ||
+                       // Middleman processes forward sync messages on a non-main thread.
+                       recordreplay::IsMiddleman());
 #ifdef MOZ_TASK_TRACER
     AutoScopedLabel autolabel("sync message %s", aMsg.name());
 #endif
 
     MessageChannel* dummy;
     MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
 
     Result rv;
--- a/toolkit/recordreplay/ipc/ChildProcess.cpp
+++ b/toolkit/recordreplay/ipc/ChildProcess.cpp
@@ -642,18 +642,20 @@ void
 ChildProcessInfo::WaitUntil(const std::function<bool()>& aCallback)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   bool sentTerminateMessage = false;
   while (!aCallback()) {
     MonitorAutoLock lock(*gMonitor);
     if (!MaybeProcessPendingMessage(this)) {
-      if (gChildrenAreDebugging) {
-        // Don't watch for hangs when children are being debugged.
+      if (gChildrenAreDebugging || IsRecording()) {
+        // Don't watch for hangs when children are being debugged. Recording
+        // children are never treated as hanged both because they cannot be
+        // restarted and because they may just be idling.
         gMonitor->Wait();
       } else {
         TimeStamp deadline = mLastMessageTime + TimeDuration::FromSeconds(HangSeconds);
         if (TimeStamp::Now() >= deadline) {
           MonitorAutoUnlock unlock(*gMonitor);
           if (!sentTerminateMessage) {
             // Try to get the child to crash, so that we can get a minidump.
             // Sending the message will reset mLastMessageTime so we get to
--- a/toolkit/recordreplay/ipc/ParentForwarding.cpp
+++ b/toolkit/recordreplay/ipc/ParentForwarding.cpp
@@ -169,16 +169,17 @@ MainThreadIsWaitingForIPDLReply()
 // Helper for places where the main thread will block while waiting on a
 // synchronous IPDL reply from a child process. Incoming messages from the
 // child must be handled immediately.
 struct MOZ_RAII AutoMarkMainThreadWaitingForIPDLReply
 {
   AutoMarkMainThreadWaitingForIPDLReply() {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     MOZ_RELEASE_ASSERT(!gMainThreadIsWaitingForIPDLReply);
+    ResumeBeforeWaitingForIPDLReply();
     gMainThreadIsWaitingForIPDLReply = true;
   }
 
   ~AutoMarkMainThreadWaitingForIPDLReply() {
     gMainThreadIsWaitingForIPDLReply = false;
   }
 };
 
--- a/toolkit/recordreplay/ipc/ParentIPC.cpp
+++ b/toolkit/recordreplay/ipc/ParentIPC.cpp
@@ -1086,16 +1086,30 @@ ResumeForwardOrBackward()
 {
   MOZ_RELEASE_ASSERT(!gChildExecuteForward || !gChildExecuteBackward);
 
   if (gResumeForwardOrBackward && (gChildExecuteForward || gChildExecuteBackward)) {
     Resume(gChildExecuteForward);
   }
 }
 
+void
+ResumeBeforeWaitingForIPDLReply()
+{
+  MOZ_RELEASE_ASSERT(gActiveChild->IsRecording());
+
+  // The main thread is about to block while it waits for a sync reply from the
+  // recording child process. If the child is paused, resume it immediately so
+  // that we don't deadlock.
+  if (gActiveChild->IsPaused()) {
+    MOZ_RELEASE_ASSERT(gChildExecuteForward);
+    Resume(true);
+  }
+}
+
 static void
 RecvHitCheckpoint(const HitCheckpointMessage& aMsg)
 {
   UpdateCheckpointTimes(aMsg);
 
   // Resume either forwards or backwards. Break the resume off into a separate
   // runnable, to avoid starving any code already on the stack and waiting for
   // the process to pause. Immediately resume if the main thread is blocked.
--- a/toolkit/recordreplay/ipc/ParentInternal.h
+++ b/toolkit/recordreplay/ipc/ParentInternal.h
@@ -36,16 +36,20 @@ bool ActiveChildIsRecording();
 
 // Get the active recording child process.
 ChildProcessInfo* ActiveRecordingChild();
 
 // Return whether the middleman's main thread is blocked waiting on a
 // synchronous IPDL reply from the recording child.
 bool MainThreadIsWaitingForIPDLReply();
 
+// If necessary, resume execution in the child before the main thread begins
+// to block while waiting on an IPDL reply from the child.
+void ResumeBeforeWaitingForIPDLReply();
+
 // Initialize state which handles incoming IPDL messages from the UI and
 // recording child processes.
 void InitializeForwarding();
 
 // Terminate all children and kill this process.
 void Shutdown();
 
 // Monitor used for synchronizing between the main and channel or message loop threads.