Bug 874437 - Only enable ipc sync wait deferred Windows message handling for plugin protocols, everything else should use standard blocking waits. r=bsmedberg, a=sledru
authorJim Mathies <jmathies@mozilla.com>
Tue, 19 Aug 2014 12:51:56 -0500
changeset 217565 61d81c3acd46ebc6550441e6a5fc08d36e8db9ee
parent 217564 f6452d9121682c90a3b56cf0f92c4d7f9b86519c
child 217566 94b1323d941f4f1765a3387de0e2fda0745bf79a
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, sledru
bugs874437
milestone33.0a2
Bug 874437 - Only enable ipc sync wait deferred Windows message handling for plugin protocols, everything else should use standard blocking waits. r=bsmedberg, a=sledru
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleParent.cpp
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/WindowsMessageLoop.cpp
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -138,16 +138,21 @@ PluginModuleChild::Init(const std::strin
                         base::ProcessHandle aParentProcessHandle,
                         MessageLoop* aIOLoop,
                         IPC::Channel* aChannel)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     GetIPCChannel()->SetAbortOnError(true);
 
+    // Request Windows message deferral behavior on our channel. This
+    // applies to the top level and all sub plugin protocols since they
+    // all share the same channel.
+    GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
 #ifdef XP_WIN
     COMMessageFilter::Initialize(this);
 #endif
 
     NS_ASSERTION(aChannel, "need a channel");
 
     if (!InitGraphics())
         return false;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -98,16 +98,21 @@ PluginModuleParent::LoadModule(const cha
     if (!launched) {
         // We never reached open
         parent->mShutdown = true;
         return nullptr;
     }
     parent->Open(parent->mSubprocess->GetChannel(),
                  parent->mSubprocess->GetChildProcessHandle());
 
+    // Request Windows message deferral behavior on our channel. This
+    // applies to the top level and all sub plugin protocols since they
+    // all share the same channel.
+    parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
     TimeoutChanged(CHILD_TIMEOUT_PREF, parent);
 
 #ifdef MOZ_CRASHREPORTER
     // If this fails, we're having IPC troubles, and we're doomed anyways.
     if (!CrashReporterParent::CreateCrashReporter(parent.get())) {
         parent->Close();
         return nullptr;
     }
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -202,17 +202,18 @@ MessageChannel::MessageChannel(MessageLi
     mPendingSyncReplies(0),
     mPendingUrgentReplies(0),
     mPendingRPCReplies(0),
     mCurrentRPCTransaction(0),
     mDispatchingSyncMessage(false),
     mDispatchingUrgentMessageCount(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
-    mAbortOnError(false)
+    mAbortOnError(false),
+    mFlags(REQUIRE_DEFAULT)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
     mIsSyncWaitingOnNonMainThread = false;
 #endif
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -58,23 +58,23 @@ class MessageChannel : HasResultCodes
     typedef mozilla::ipc::Transport Transport;
 
     MessageChannel(MessageListener *aListener);
     ~MessageChannel();
 
     // "Open" from the perspective of the transport layer; the underlying
     // socketpair/pipe should already be created.
     //
-    // Returns true iff the transport layer was successfully connected,
+    // Returns true if the transport layer was successfully connected,
     // i.e., mChannelState == ChannelConnected.
     bool Open(Transport* aTransport, MessageLoop* aIOLoop=0, Side aSide=UnknownSide);
 
     // "Open" a connection to another thread in the same process.
     //
-    // Returns true iff the transport layer was successfully connected,
+    // Returns true if the transport layer was successfully connected,
     // i.e., mChannelState == ChannelConnected.
     //
     // For more details on the process of opening a channel between
     // threads, see the extended comment on this function
     // in MessageChannel.cpp.
     bool Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide);
 
     // Close the underlying transport channel.
@@ -84,16 +84,29 @@ class MessageChannel : HasResultCodes
     // for process links only, not thread links.
     void CloseWithError();
 
     void SetAbortOnError(bool abort)
     {
         mAbortOnError = true;
     }
 
+    // Misc. behavioral traits consumers can request for this channel
+    enum ChannelFlags {
+      REQUIRE_DEFAULT                         = 0,
+      // Windows: if this channel operates on the UI thread, indicates
+      // WindowsMessageLoop code should enable deferred native message
+      // handling to prevent deadlocks. Should only be used for protocols
+      // that manage child processes which might create native UI, like
+      // plugins.
+      REQUIRE_DEFERRED_MESSAGE_PROTECTION     = 1 << 0
+    };
+    void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
+    ChannelFlags GetChannelFlags() { return mFlags; }
+
     // Asynchronously send a message to the other side of the channel
     bool Send(Message* aMsg);
 
     // Asynchronously deliver a message back to this side of the
     // channel
     bool Echo(Message* aMsg);
 
     // Synchronously send |msg| (i.e., wait for |reply|)
@@ -634,16 +647,19 @@ class MessageChannel : HasResultCodes
 
 #ifdef OS_WIN
     HANDLE mEvent;
 #endif
 
     // Should the channel abort the process from the I/O thread when
     // a channel error occurs?
     bool mAbortOnError;
+
+    // See SetChannelFlags
+    ChannelFlags mFlags;
 };
 
 bool
 ProcessingUrgentMessages();
 
 } // namespace ipc
 } // namespace mozilla
 
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -630,42 +630,44 @@ InitUIThread()
   MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
              "Called InitUIThread multiple times on different threads!");
 }
 
 } // namespace windows
 } // namespace ipc
 } // namespace mozilla
 
+// See SpinInternalEventLoop below
 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
   : mInterrupt(interrupt)
   , mSpinNestedEvents(false)
   , mListenerNotified(false)
   , mChannel(channel)
   , mPrev(mChannel->mTopFrame)
   , mStaticPrev(sStaticTopFrame)
 {
-  // Only track stack frames when we are on the gui thread.
-  if (GetCurrentThreadId() != gUIThreadId) {
+  // Only track stack frames when Windows message deferral behavior
+  // is request for the channel.
+  if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     return;
   }
 
   mChannel->mTopFrame = this;
   sStaticTopFrame = this;
 
   if (!mStaticPrev) {
     NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
     gNeuteredWindows = new nsAutoTArray<HWND, 20>();
     NS_ASSERTION(gNeuteredWindows, "Out of memory!");
   }
 }
 
 MessageChannel::SyncStackFrame::~SyncStackFrame()
 {
-  if (GetCurrentThreadId() != gUIThreadId) {
+  if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     return;
   }
 
   NS_ASSERTION(this == mChannel->mTopFrame,
                "Mismatched interrupt stack frames");
   NS_ASSERTION(this == sStaticTopFrame,
                "Mismatched static Interrupt stack frames");
 
@@ -782,21 +784,19 @@ IsTimeoutExpired(PRIntervalTime aStart, 
 
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
-  // We jump through a lot of unique hoops in dealing with Windows message
-  // trapping to prevent re-entrancy when we are blocked waiting on a sync
-  // reply or new rpc in-call. However none of this overhead is needed when
-  // we aren't on the main (gui) thread. 
-  if (GetCurrentThreadId() != gUIThreadId) {
+  // Use a blocking wait if this channel does not require
+  // Windows message deferral behavior.
+  if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
                              PR_INTERVAL_NO_TIMEOUT :
                              PR_MillisecondsToInterval(mTimeoutMs);
     PRIntervalTime waitStart = 0;
 
     if (timeout != PR_INTERVAL_NO_TIMEOUT) {
       waitStart = PR_IntervalNow();
     }
@@ -810,19 +810,18 @@ MessageChannel::WaitForSyncNotify()
     mIsSyncWaitingOnNonMainThread = false;
 
     // If the timeout didn't expire, we know we received an event. The
     // converse is not true.
     return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ?
                         false : IsTimeoutExpired(waitStart, timeout));
   }
 
-  NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
-               "Shouldn't be on a non-main thread in here!");
-
+  NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
+               "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
   UINT_PTR timerId = 0;
@@ -934,31 +933,29 @@ MessageChannel::WaitForSyncNotify()
 
 bool
 MessageChannel::WaitForInterruptNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
-  // Re-use sync notification wait code when we aren't on the
-  // gui thread, which bypasses the gui thread hoops we jump
-  // through in dealing with Windows messaging.
-  if (GetCurrentThreadId() != gUIThreadId) {
+  // Re-use sync notification wait code if this channel does not require
+  // Windows message deferral behavior. 
+  if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
     return WaitForSyncNotify();
   }
 
-  NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
-               "Shouldn't be on a non-main thread in here!");
-
   if (!InterruptStackDepth()) {
     // There is currently no way to recover from this condition.
     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
+  NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
+               "Shouldn't be here for channels that don't use message deferral!");
   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
   UINT_PTR timerId = 0;