Bug 1273635: Enable alertable waits in content process main thread; r=jimm
☠☠ backed out by 0d97ff06ef63 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Wed, 11 May 2016 12:49:49 -0600
changeset 306853 48d06bcb9c65661aa8eae4d39ff452d22c2403d3
parent 306852 23d63b3b2c751ec56b92c2f52c8cfd3a13096145
child 306854 321ad9fd9a84770b2ef6a22301254bc52a349696
push id30501
push usercbook@mozilla.com
push dateThu, 28 Jul 2016 15:41:40 +0000
treeherdermozilla-central@afb47dfb71ed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1273635
milestone50.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 1273635: Enable alertable waits in content process main thread; r=jimm MozReview-Commit-ID: 2qGdGj41M0n
dom/ipc/ContentChild.cpp
ipc/glue/MessageChannel.h
ipc/glue/WindowsMessageLoop.cpp
widget/windows/WinUtils.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -639,16 +639,19 @@ ContentChild::Init(MessageLoop* aIOLoop,
 
   // If communications with the parent have broken down, take the process
   // down so it's not hanging around.
   bool abortOnError = true;
 #ifdef MOZ_NUWA_PROCESS
   abortOnError &= !IsNuwaProcess();
 #endif
   GetIPCChannel()->SetAbortOnError(abortOnError);
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY);
+#endif
 
 #ifdef MOZ_X11
   // Send the parent our X socket to act as a proxy reference for our X
   // resources.
   int xSocketFd = ConnectionNumber(DefaultXDisplay());
   SendBackUpXResources(FileDescriptor(xSocketFd));
 #endif
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -122,17 +122,22 @@ class MessageChannel : HasResultCodes
     // 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
+      REQUIRE_DEFERRED_MESSAGE_PROTECTION     = 1 << 0,
+      // Windows: When this flag is specified, any wait that occurs during
+      // synchronous IPC will be alertable, thus allowing a11y code in the
+      // chrome process to reenter content while content is waiting on a
+      // synchronous call.
+      REQUIRE_A11Y_REENTRY                    = 1 << 1,
     };
     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
@@ -250,17 +255,20 @@ class MessageChannel : HasResultCodes
     static SyncStackFrame* sStaticTopFrame;
 
   public:
     void ProcessNativeEventsInInterruptCall();
     static void NotifyGeckoEventDispatch();
 
   private:
     void SpinInternalEventLoop();
-#endif
+#if defined(ACCESSIBILITY)
+    bool WaitForSyncNotifyWithA11yReentry();
+#endif // defined(ACCESSIBILITY)
+#endif // defined(OS_WIN)
 
   private:
     void CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide);
     void OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide);
 
     void PostErrorNotifyTask();
     void OnNotifyMaybeChannelError();
     void ReportConnectionError(const char* aChannelName, Message* aMsg = nullptr) const;
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -1051,23 +1051,82 @@ SuppressedNeuteringRegion::~SuppressedNe
   if (mReenable) {
     MOZ_ASSERT(sSuppressNeutering);
     sSuppressNeutering = false;
   }
 }
 
 bool SuppressedNeuteringRegion::sSuppressNeutering = false;
 
+#if defined(ACCESSIBILITY)
+bool
+MessageChannel::WaitForSyncNotifyWithA11yReentry()
+{
+  mMonitor->AssertCurrentThreadOwns();
+  MonitorAutoUnlock unlock(*mMonitor);
+
+  const DWORD waitStart = ::GetTickCount();
+  DWORD elapsed = 0;
+  DWORD timeout = mTimeoutMs == kNoTimeout ? INFINITE :
+                  static_cast<DWORD>(mTimeoutMs);
+  bool timedOut = false;
+
+  while (true) {
+    { // Scope for lock
+      MonitorAutoLock lock(*mMonitor);
+      if (!Connected()) {
+        break;
+      }
+    }
+    if (timeout != static_cast<DWORD>(kNoTimeout)) {
+      elapsed = ::GetTickCount() - waitStart;
+    }
+    if (elapsed >= timeout) {
+      timedOut = true;
+      break;
+    }
+    DWORD waitResult = 0;
+    ::SetLastError(ERROR_SUCCESS);
+    HRESULT hr = ::CoWaitForMultipleHandles(COWAIT_ALERTABLE,
+                                            timeout - elapsed,
+                                            1, &mEvent, &waitResult);
+    if (hr == RPC_S_CALLPENDING) {
+      timedOut = true;
+      break;
+    }
+    if (hr == S_OK) {
+      if (waitResult == 0) {
+        // mEvent is signaled
+        break;
+      }
+      if (waitResult == WAIT_IO_COMPLETION) {
+        // APC fired, keep waiting
+        continue;
+      }
+    }
+    NS_WARN_IF_FALSE(SUCCEEDED(hr), "CoWaitForMultipleHandles failed");
+  }
+
+  return WaitResponse(timedOut);
+}
+#endif
+
 bool
 MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
+#if defined(ACCESSIBILITY)
+  if (mFlags & REQUIRE_A11Y_REENTRY) {
+    return WaitForSyncNotifyWithA11yReentry();
+  }
+#endif
+
   // Use a blocking wait if this channel does not require
   // Windows message deferral behavior.
   if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
     PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
                              PR_INTERVAL_NO_TIMEOUT :
                              PR_MillisecondsToInterval(mTimeoutMs);
     PRIntervalTime waitStart = 0;
 
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -721,36 +721,56 @@ WinUtils::GetMessage(LPMSG aMsg, HWND aW
                                       &ret);
     NS_ENSURE_TRUE(SUCCEEDED(hr), false);
     return ret;
   }
 #endif // #ifdef NS_ENABLE_TSF
   return ::GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage);
 }
 
+#if defined(ACCESSIBILITY)
+static DWORD
+GetWaitFlags()
+{
+  DWORD result = MWMO_INPUTAVAILABLE;
+  if (XRE_IsContentProcess()) {
+    result |= MWMO_ALERTABLE;
+  }
+  return result;
+}
+#endif
+
 /* static */
 void
 WinUtils::WaitForMessage(DWORD aTimeoutMs)
 {
+#if defined(ACCESSIBILITY)
+  static const DWORD waitFlags = GetWaitFlags();
+#else
+  const DWORD waitFlags = MWMO_INPUTAVAILABLE;
+#endif
+
   const DWORD waitStart = ::GetTickCount();
   DWORD elapsed = 0;
   while (true) {
     if (aTimeoutMs != INFINITE) {
       elapsed = ::GetTickCount() - waitStart;
     }
     if (elapsed >= aTimeoutMs) {
       break;
     }
     DWORD result = ::MsgWaitForMultipleObjectsEx(0, NULL, aTimeoutMs - elapsed,
-                                                 MOZ_QS_ALLEVENT,
-                                                 MWMO_INPUTAVAILABLE);
+                                                 MOZ_QS_ALLEVENT, waitFlags);
     NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed");
     if (result == WAIT_TIMEOUT) {
       break;
     }
+    if (result == WAIT_IO_COMPLETION) {
+      continue;
+    }
 
     // Sent messages (via SendMessage and friends) are processed differently
     // than queued messages (via PostMessage); the destination window procedure
     // of the sent message is called during (Get|Peek)Message. Since PeekMessage
     // does not tell us whether it processed any sent messages, we need to query
     // this ahead of time.
     bool haveSentMessagesPending =
       (HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;