Bug 1488808 Part 16 - Avoid deadlocking in a few places after diverging from the recording, r=froydnj.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 17 Oct 2018 10:15:41 -0600
changeset 442021 09a979b6e583eab5833722f358c3a26e0b89a3a2
parent 442020 0fa982d6f06a2739f7667e462946b9c80739c3a8
child 442022 7f2e0b3603b4edd4b540ba394943b65e5e44fb41
push id34885
push usercsabou@mozilla.com
push dateFri, 19 Oct 2018 04:17:46 +0000
treeherdermozilla-central@7d74c5905384 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1488808
milestone64.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 1488808 Part 16 - Avoid deadlocking in a few places after diverging from the recording, r=froydnj.
ipc/glue/MessageChannel.cpp
toolkit/recordreplay/ipc/ChildIPC.cpp
toolkit/recordreplay/ipc/ChildIPC.h
toolkit/recordreplay/ipc/DisabledIPC.cpp
xpcom/threads/ThreadEventTarget.cpp
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -990,16 +990,27 @@ MessageChannel::Echo(Message* aMsg)
 
     mLink->EchoMessage(msg.release());
     return true;
 }
 
 bool
 MessageChannel::Send(Message* aMsg)
 {
+    if (recordreplay::HasDivergedFromRecording() &&
+        recordreplay::child::SuppressMessageAfterDiverge(aMsg))
+    {
+        // Only certain IPDL messages are allowed to be sent in a replaying
+        // process after it has diverged from the recording, to avoid
+        // deadlocking with threads that remain idle. The browser remains
+        // paused after diverging from the recording, and other IPDL messages
+        // do not need to be sent.
+        return true;
+    }
+
     if (aMsg->size() >= kMinTelemetryMessageSize) {
         Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size());
     }
 
     // If the message was created by the IPC bindings, the create time will be
     // recorded. Use this information to report the IPC_WRITE_MAIN_THREAD_LATENCY_MS (time
     // from message creation to it being sent).
     if (NS_IsMainThread() && aMsg->create_time()) {
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -11,17 +11,20 @@
 
 #include "base/message_loop.h"
 #include "base/task.h"
 #include "chrome/common/child_thread.h"
 #include "chrome/common/mach_ipc_mac.h"
 #include "ipc/Channel.h"
 #include "mac/handler/exception_handler.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/VsyncDispatcher.h"
 
 #include "InfallibleVector.h"
 #include "MemorySnapshot.h"
 #include "nsPrintfCString.h"
 #include "ParentInternal.h"
 #include "ProcessRecordReplay.h"
@@ -586,16 +589,39 @@ static bool
 CompositorCanPerformMiddlemanCalls()
 {
   // After repainting finishes the compositor is not allowed to send call
   // requests to the middleman anymore.
   MonitorAutoLock lock(*gMonitor);
   return gNumPendingPaints != 0;
 }
 
+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
+  // need a more robust way to deal with this problem).
+
+  IPC::Message::msgid_t type = aMsg->type();
+  if (type >= layers::PLayerTransaction::PLayerTransactionStart &&
+      type <= layers::PLayerTransaction::PLayerTransactionEnd) {
+    return false;
+  }
+
+  if (type == layers::PCompositorBridge::Msg_PTextureConstructor__ID) {
+    return false;
+  }
+
+  return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Checkpoint Messages
 ///////////////////////////////////////////////////////////////////////////////
 
 // When recording, the time when the last HitCheckpoint message was sent.
 static double gLastCheckpointTime;
 
 // When recording and we are idle, the time when we became idle.
--- a/toolkit/recordreplay/ipc/ChildIPC.h
+++ b/toolkit/recordreplay/ipc/ChildIPC.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_recordreplay_ChildIPC_h
 #define mozilla_recordreplay_ChildIPC_h
 
 #include "base/process.h"
 #include "mozilla/gfx/2D.h"
 #include "Units.h"
 
+namespace IPC { class Message; }
+
 namespace mozilla {
 
 class VsyncObserver;
 
 namespace recordreplay {
 namespace child {
 
 // This file has the public API for definitions used in facilitating IPC
@@ -48,13 +50,17 @@ void OnVsync();
 // about to happen, the main thread calls NotifyPaintStart, and when the
 // compositor thread finishes the paint it calls NotifyPaintComplete.
 void NotifyPaintStart();
 void NotifyPaintComplete();
 
 // Get a draw target which the compositor thread can paint to.
 already_AddRefed<gfx::DrawTarget> DrawTargetForRemoteDrawing(LayoutDeviceIntSize aSize);
 
+// Called to ignore IPDL messages sent after diverging from the recording,
+// except for those needed for compositing.
+bool SuppressMessageAfterDiverge(IPC::Message* aMsg);
+
 } // namespace child
 } // namespace recordreplay
 } // namespace mozilla
 
 #endif // mozilla_recordreplay_ChildIPC_h
--- a/toolkit/recordreplay/ipc/DisabledIPC.cpp
+++ b/toolkit/recordreplay/ipc/DisabledIPC.cpp
@@ -70,16 +70,22 @@ NotifyPaintComplete()
 }
 
 already_AddRefed<gfx::DrawTarget>
 DrawTargetForRemoteDrawing(LayoutDeviceIntSize aSize)
 {
   MOZ_CRASH();
 }
 
+bool
+SuppressMessageAfterDiverge(IPC::Message* aMsg)
+{
+  MOZ_CRASH();
+}
+
 } // namespace child
 
 namespace parent {
 
 void
 InitializeUIProcess(int aArgc, char** aArgv)
 {
   // This is called from UI processes, and has no state to initialize if
--- a/xpcom/threads/ThreadEventTarget.cpp
+++ b/xpcom/threads/ThreadEventTarget.cpp
@@ -129,16 +129,24 @@ ThreadEventTarget::Dispatch(already_AddR
     return NS_ERROR_INVALID_ARG;
   }
 
   if (gXPCOMThreadsShutDown && !mIsMainThread) {
     NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   }
 
+  // Don't dispatch runnables to other threads when replaying and diverged from
+  // the recording, to avoid deadlocking with other idle threads. The browser
+  // remains paused after diverging from the recording, and threads will not
+  // run their event loops.
+  if (recordreplay::HasDivergedFromRecording()) {
+    return NS_ERROR_FAILURE;
+  }
+
 #ifdef MOZ_TASK_TRACER
   nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
   (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
   // XXX tracedRunnable will always leaked when we fail to disptch.
   event = tracedRunnable.forget();
 #endif
 
   if (aFlags & DISPATCH_SYNC) {