Bug 1009590: Deal with non-ui-thread IPDL usage on Windows. r=bent
authorBas Schouten <bschouten@mozilla.com>
Sun, 18 May 2014 05:16:51 +0200
changeset 203998 115aeaefd16bf412836f253da41e42aed39a60e9
parent 203997 fda65b2f990a53bd193026460f15e82626e15e68
child 203999 c157c41d3866999acb152f31057d5a122f37762f
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs1009590
milestone32.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 1009590: Deal with non-ui-thread IPDL usage on Windows. r=bent
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/WindowsMessageLoop.cpp
ipc/glue/WindowsMessageLoop.h
ipc/glue/moz.build
toolkit/xre/nsEmbedFunctions.cpp
widget/windows/nsAppShell.cpp
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -206,16 +206,17 @@ MessageChannel::MessageChannel(MessageLi
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
     mAbortOnError(false)
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
+    mIsSyncWaitingOnNonMainThread = false;
 #endif
 
     mDequeueOneTask = new RefCountedTask(NewRunnableMethod(
                                                  this,
                                                  &MessageChannel::OnMaybeDequeueOne));
 
 #ifdef OS_WIN
     mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -155,16 +155,18 @@ class MessageChannel : HasResultCodes
         }
         return false;
     }
 
   protected:
     // The deepest sync stack frame for this channel.
     SyncStackFrame* mTopFrame;
 
+    bool mIsSyncWaitingOnNonMainThread;
+
     // The deepest sync stack frame on any channel.
     static SyncStackFrame* sStaticTopFrame;
 
   public:
     void ProcessNativeEventsInInterruptCall();
     static void NotifyGeckoEventDispatch();
 
   private:
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -535,29 +535,16 @@ UnhookNeuteredWindows()
     return;
   uint32_t count = gNeuteredWindows->Length();
   for (uint32_t index = 0; index < count; index++) {
     RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
   }
   gNeuteredWindows->Clear();
 }
 
-void
-Init()
-{
-  // If we aren't setup before a call to NotifyWorkerThread, we'll hang
-  // on startup.
-  if (!gUIThreadId) {
-    gUIThreadId = GetCurrentThreadId();
-  }
-  NS_ASSERTION(gUIThreadId, "ThreadId should not be 0!");
-  NS_ASSERTION(gUIThreadId == GetCurrentThreadId(),
-               "Running on different threads!");
-}
-
 // This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
 // value for GetTickCount(), which is something like 50 days). It uses the
 // cheapest (and least accurate) method supported by Windows 2000.
 
 struct TimeoutData
 {
   DWORD startTicks;
   DWORD targetTicks;
@@ -589,16 +576,37 @@ TimeoutHasExpired(const TimeoutData& aDa
     // Overflow
     return now < aData.startTicks && now >= aData.targetTicks;
   }
   return now >= aData.targetTicks;
 }
 
 } // anonymous namespace
 
+namespace mozilla {
+namespace ipc {
+namespace windows {
+
+void
+InitUIThread()
+{
+  // If we aren't setup before a call to NotifyWorkerThread, we'll hang
+  // on startup.
+  if (!gUIThreadId) {
+    gUIThreadId = GetCurrentThreadId();
+  }
+  MOZ_ASSERT(gUIThreadId);
+  MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
+             "Called InitUIThread multiple times on different threads!");
+}
+
+} // namespace windows
+} // namespace ipc
+} // namespace mozilla
+
 MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
   : mInterrupt(interrupt)
   , mSpinNestedEvents(false)
   , mListenerNotified(false)
   , mChannel(channel)
   , mPrev(mChannel->mTopFrame)
   , mStaticPrev(sStaticTopFrame)
 {
@@ -716,23 +724,52 @@ MessageChannel::SpinInternalEventLoop()
                                              QS_ALLINPUT);
     if (result == WAIT_OBJECT_0) {
       // Our NotifyWorkerThread event was signaled
       return;
     }
   } while (true);
 }
 
+static inline bool
+IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
+{
+  return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
+    (aTimeout <= (PR_IntervalNow() - aStart));
+}
+
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
-  // Initialize global objects used in deferred messaging.
-  Init();
+  MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
+
+  if (GetCurrentThreadId() != gUIThreadId) {
+    PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
+                             PR_INTERVAL_NO_TIMEOUT :
+                             PR_MillisecondsToInterval(mTimeoutMs);
+    PRIntervalTime waitStart = 0;
+
+    if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+      waitStart = PR_IntervalNow();
+    }
+
+    MOZ_ASSERT(!mIsSyncWaitingOnNonMainThread);
+    mIsSyncWaitingOnNonMainThread = true;
+
+    mMonitor->Wait(timeout);
+
+    MOZ_ASSERT(mIsSyncWaitingOnNonMainThread);
+    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(mTopFrame && !mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
@@ -843,24 +880,27 @@ MessageChannel::WaitForSyncNotify()
   return WaitResponse(timedout);
 }
 
 bool
 MessageChannel::WaitForInterruptNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
+  MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
+
+  if (GetCurrentThreadId() != gUIThreadId) {
+    return WaitForSyncNotify();
+  }
+
   if (!InterruptStackDepth()) {
     // There is currently no way to recover from this condition.
     NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
-  // Initialize global objects used in deferred messaging.
-  Init();
-
   NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
                "Top frame is not a sync frame!");
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
   UINT_PTR timerId = 0;
@@ -982,16 +1022,22 @@ MessageChannel::WaitForInterruptNotify()
 
   return WaitResponse(timedout);
 }
 
 void
 MessageChannel::NotifyWorkerThread()
 {
   mMonitor->AssertCurrentThreadOwns();
+
+  if (mIsSyncWaitingOnNonMainThread) {
+    mMonitor->Notify();
+    return;
+  }
+
   NS_ASSERTION(mEvent, "No signal event to set, this is really bad!");
   if (!SetEvent(mEvent)) {
     NS_WARNING("Failed to set NotifyWorkerThread event!");
   }
 }
 
 void
 DeferredSendMessage::Run()
--- a/ipc/glue/WindowsMessageLoop.h
+++ b/ipc/glue/WindowsMessageLoop.h
@@ -13,16 +13,18 @@
 
 #include "base/basictypes.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace ipc {
 namespace windows {
 
+void InitUIThread();
+
 class DeferredMessage
 {
 public:
   DeferredMessage()
   {
     MOZ_COUNT_CTOR(DeferredMessage);
   }
 
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -26,16 +26,17 @@ EXPORTS.mozilla.ipc += [
     'ProtocolUtils.h',
     'ScopedXREEmbed.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',
     'SharedMemorySysV.h',
     'Shmem.h',
     'Transport.h',
     'URIUtils.h',
+    'WindowsMessageLoop.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla.ipc += [
         'Transport_win.h',
     ]
     SOURCES += [
         'SharedMemory_windows.cpp',
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -23,16 +23,17 @@
 #include "nsIAppStartupNotifier.h"
 #include "nsIDirectoryService.h"
 #include "nsIFile.h"
 #include "nsIToolkitChromeRegistry.h"
 #include "nsIToolkitProfile.h"
 
 #ifdef XP_WIN
 #include <process.h>
+#include "mozilla/ipc/WindowsMessageLoop.h"
 #endif
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
 #include "nsAutoRef.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExceptionHandler.h"
 #include "nsString.h"
@@ -468,16 +469,20 @@ XRE_InitChildProcess(int aArgc,
     // spurious warnings about XPCOM objects being destroyed from a
     // static context.
 
     // Associate this thread with a UI MessageLoop
     MessageLoop uiMessageLoop(uiLoopType);
     {
       nsAutoPtr<ProcessChild> process;
 
+#ifdef XP_WIN
+      mozilla::ipc::windows::InitUIThread();
+#endif
+
       switch (aProcess) {
       case GeckoProcessType_Default:
         NS_RUNTIMEABORT("This makes no sense");
         break;
 
       case GeckoProcessType_Plugin:
         process = new PluginProcessChild(parentHandle);
         break;
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/WindowsMessageLoop.h"
 #include "nsAppShell.h"
 #include "nsToolkit.h"
 #include "nsThreadUtils.h"
 #include "WinUtils.h"
 #include "WinTaskbar.h"
 #include "WinMouseScrollHandler.h"
 #include "nsWindowDefs.h"
 #include "nsString.h"
@@ -139,16 +140,18 @@ nsresult
 nsAppShell::Init()
 {
 #ifdef MOZ_CRASHREPORTER
   LSPAnnotate();
 #endif
 
   mLastNativeEventScheduled = TimeStamp::NowLoRes();
 
+  mozilla::ipc::windows::InitUIThread();
+
   sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
   NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
 
   WNDCLASSW wc;
   HINSTANCE module = GetModuleHandle(nullptr);
 
   const wchar_t *const kWindowClass = L"nsAppShell:EventWindowClass";
   if (!GetClassInfoW(module, kWindowClass, &wc)) {