Bug 1060738 - Implement MessagePumpForNonMainUIThreads for Windows, a xpcom compatible subclass of chromium's MessagePumpForUI. r=tabraldes a=sylvestre
authorJim Mathies <jmathies@mozilla.com>
Fri, 12 Sep 2014 09:49:38 -0500
changeset 216720 b6a5a3973477
parent 216719 2c6a2069023a
child 216721 1355bb2a2765
push id3890
push userrjesup@wgate.com
push date2014-09-15 14:07 +0000
treeherdermozilla-beta@84daded3719c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstabraldes, sylvestre
bugs1060738
milestone33.0
Bug 1060738 - Implement MessagePumpForNonMainUIThreads for Windows, a xpcom compatible subclass of chromium's MessagePumpForUI. r=tabraldes a=sylvestre
ipc/chromium/src/base/message_loop.cc
ipc/chromium/src/base/message_loop.h
ipc/chromium/src/base/message_pump_win.h
ipc/glue/MessagePump.cpp
ipc/glue/MessagePump.h
media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -102,33 +102,38 @@ MessageLoop::MessageLoop(Type type)
 #ifdef OS_WIN
       os_modal_loop_(false),
 #endif  // OS_WIN
       transient_hang_timeout_(0),
       permanent_hang_timeout_(0),
       next_sequence_num_(0) {
   DCHECK(!current()) << "should only have one message loop per thread";
   lazy_tls_ptr.Pointer()->Set(this);
-  if (type_ == TYPE_MOZILLA_UI) {
+
+  switch (type_) {
+  case TYPE_MOZILLA_UI:
     pump_ = new mozilla::ipc::MessagePump();
     return;
-  }
-  if (type_ == TYPE_MOZILLA_CHILD) {
+  case TYPE_MOZILLA_CHILD:
     pump_ = new mozilla::ipc::MessagePumpForChildProcess();
     // There is a MessageLoop Run call from XRE_InitChildProcess
     // and another one from MessagePumpForChildProcess. The one
     // from MessagePumpForChildProcess becomes the base, so we need
     // to set run_depth_base_ to 2 or we'll never be able to process
     // Idle tasks.
     run_depth_base_ = 2;
     return;
-  }
-  if (type_ == TYPE_MOZILLA_NONMAINTHREAD) {
+  case TYPE_MOZILLA_NONMAINTHREAD:
     pump_ = new mozilla::ipc::MessagePumpForNonMainThreads();
     return;
+#if defined(OS_WIN)
+  case TYPE_MOZILLA_NONMAINUITHREAD:
+    pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads();
+    return;
+#endif
   }
 
 #if defined(OS_WIN)
   // TODO(rvargas): Get rid of the OS guards.
   if (type_ == TYPE_DEFAULT) {
     pump_ = new base::MessagePumpDefault();
   } else if (type_ == TYPE_IO) {
     pump_ = new base::MessagePumpForIO();
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -202,23 +202,28 @@ public:
   // TYPE_MOZILLA_UI
   //   This type of ML is used in Mozilla parent processes which initialize
   //   XPCOM and use the gecko event loop.
   //
   // TYPE_MOZILLA_NONMAINTHREAD
   //   This type of ML is used in Mozilla parent processes which initialize
   //   XPCOM and use the nsThread event loop.
   //
+  // TYPE_MOZILLA_NONMAINUITHREAD
+  //   This type of ML is used in Mozilla processes which initialize XPCOM
+  //   and use TYPE_UI loop logic.
+  //
   enum Type {
     TYPE_DEFAULT,
     TYPE_UI,
     TYPE_IO,
     TYPE_MOZILLA_CHILD,
     TYPE_MOZILLA_UI,
-    TYPE_MOZILLA_NONMAINTHREAD
+    TYPE_MOZILLA_NONMAINTHREAD,
+    TYPE_MOZILLA_NONMAINUITHREAD
   };
 
   // Normally, it is not necessary to instantiate a MessageLoop.  Instead, it
   // is typical to make use of the current thread's MessageLoop instance.
   explicit MessageLoop(Type type = TYPE_DEFAULT);
   ~MessageLoop();
 
   // Returns the type passed to the constructor.
--- a/ipc/chromium/src/base/message_pump_win.h
+++ b/ipc/chromium/src/base/message_pump_win.h
@@ -164,30 +164,33 @@ class MessagePumpForUI : public MessageP
   virtual void ScheduleWork();
   virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
 
   // Applications can call this to encourage us to process all pending WM_PAINT
   // messages.  This method will process all paint messages the Windows Message
   // queue can provide, up to some fixed number (to avoid any infinite loops).
   void PumpOutPendingPaintMessages();
 
- private:
-  static LRESULT CALLBACK WndProcThunk(
-      HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+protected:
   virtual void DoRunLoop();
+
+  bool ProcessNextWindowsMessage();
   void InitMessageWnd();
   void WaitForWork();
   void HandleWorkMessage();
   void HandleTimerMessage();
-  bool ProcessNextWindowsMessage();
   bool ProcessMessageHelper(const MSG& msg);
   bool ProcessPumpReplacementMessage();
 
   // A hidden message-only window.
   HWND message_hwnd_;
+
+ private:
+  static LRESULT CALLBACK WndProcThunk(
+      HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
 };
 
 //-----------------------------------------------------------------------------
 // MessagePumpForIO extends MessagePumpWin with methods that are particular to a
 // MessageLoop instantiated with TYPE_IO. This version of MessagePump does not
 // deal with Windows mesagges, and instead has a Run loop based on Completion
 // Ports so it is better suited for IO operations.
 //
--- a/ipc/glue/MessagePump.cpp
+++ b/ipc/glue/MessagePump.cpp
@@ -313,18 +313,31 @@ MessagePumpForNonMainThreads::Run(base::
 
   mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
   MOZ_ASSERT(mDelayedWorkTimer);
 
   if (NS_FAILED(mDelayedWorkTimer->SetTarget(mThread))) {
     MOZ_CRASH("Failed to set timer target!");
   }
 
+  // Chromium event notifications to be processed will be received by this
+  // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that
+  // were received before our mThread is valid, however, will not generate
+  // runnable wrappers. We must process any of these before we enter this
+  // loop, or we will forever have unprocessed chromium messages in our queue.
+  //
+  // Note we would like to request a flush of the chromium event queue
+  // using a runnable on the xpcom side, but some thread implementations
+  // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork
+  // calls dispatch on mThread) before the thread processes an event. As
+  // such, clear the queue manually.
+  while (aDelegate->DoWork()) {
+  }
+
   base::ScopedNSAutoreleasePool autoReleasePool;
-
   for (;;) {
     autoReleasePool.Recycle();
 
     bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false;
     if (!keep_running_) {
       break;
     }
 
@@ -354,8 +367,97 @@ MessagePumpForNonMainThreads::Run(base::
     // This will either sleep or process an event.
     NS_ProcessNextEvent(mThread, true);
   }
 
   mDelayedWorkTimer->Cancel();
 
   keep_running_ = true;
 }
+
+#if defined(XP_WIN)
+
+NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads, nsIThreadObserver)
+
+#define CHECK_QUIT_STATE { if (state_->should_quit) { break; } }
+
+void MessagePumpForNonMainUIThreads::DoRunLoop()
+{
+  // If this is a chromium thread and no nsThread is associated
+  // with it, this call will create a new nsThread.
+  mThread = NS_GetCurrentThread();
+  MOZ_ASSERT(mThread);
+
+  // Set the main thread observer so we can wake up when
+  // xpcom events need to get processed.
+  nsCOMPtr<nsIThreadInternal> ti(do_QueryInterface(mThread));
+  MOZ_ASSERT(ti);
+  ti->SetObserver(this);
+
+  base::ScopedNSAutoreleasePool autoReleasePool;
+  for (;;) {
+    autoReleasePool.Recycle();
+
+    bool didWork = NS_ProcessNextEvent(mThread, false);
+
+    didWork |= ProcessNextWindowsMessage();
+    CHECK_QUIT_STATE
+
+    didWork |= state_->delegate->DoWork();
+    CHECK_QUIT_STATE
+
+    didWork |= state_->delegate->DoDelayedWork(&delayed_work_time_);
+    if (didWork && delayed_work_time_.is_null()) {
+      KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+    }
+    CHECK_QUIT_STATE
+
+    if (didWork) {
+      continue;
+    }
+
+    didWork = state_->delegate->DoIdleWork();
+    CHECK_QUIT_STATE
+
+    SetInWait();
+    bool hasWork = NS_HasPendingEvents(mThread);
+    if (didWork || hasWork) {
+      ClearInWait();
+      continue;
+    }
+    WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)
+    ClearInWait();
+  }
+
+  ClearInWait();
+
+  ti->SetObserver(nullptr);
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal *thread)
+{
+  // If our thread is sleeping in DoRunLoop's call to WaitForWork() and an
+  // event posts to the nsIThread event queue - break our thread out of
+  // chromium's WaitForWork.
+  if (GetInWait()) {
+    ScheduleWork();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread,
+                                                   bool mayWait,
+                                                   uint32_t recursionDepth)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread,
+                                                      uint32_t recursionDepth,
+                                                      bool eventWasProcessed)
+{
+  return NS_OK;
+}
+
+#endif // XP_WIN
--- a/ipc/glue/MessagePump.h
+++ b/ipc/glue/MessagePump.h
@@ -1,20 +1,26 @@
 /* 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/. */
 
 #ifndef __IPC_GLUE_MESSAGEPUMP_H__
 #define __IPC_GLUE_MESSAGEPUMP_H__
 
 #include "base/message_pump_default.h"
+#if defined(XP_WIN)
+#include "base/message_pump_win.h"
+#endif
+
 #include "base/time.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
+#include "nsIThreadInternal.h"
 
 class nsIThread;
 class nsITimer;
 
 namespace mozilla {
 namespace ipc {
 
 class DoWorkRunnable;
@@ -84,12 +90,71 @@ public:
 
   virtual void Run(base::MessagePump::Delegate* aDelegate) MOZ_OVERRIDE;
 
 private:
   ~MessagePumpForNonMainThreads()
   { }
 };
 
+#if defined(XP_WIN)
+// Extends the TYPE_UI message pump to process xpcom events. Currently only
+// implemented for Win.
+class MessagePumpForNonMainUIThreads MOZ_FINAL:
+  public base::MessagePumpForUI,
+  public nsIThreadObserver
+{
+public:
+  // We don't want xpcom refing, chromium controls our lifetime via
+  // RefCountedThreadSafe.
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) {
+    return 2;
+  }
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) {
+    return 1;
+  }
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
+
+  NS_DECL_NSITHREADOBSERVER
+
+public:
+  MessagePumpForNonMainUIThreads() :
+    mThread(nullptr),
+    mInWait(false),
+    mWaitLock("mInWait")
+  {
+  }
+
+  // The main run loop for this thread.
+  virtual void DoRunLoop();
+
+protected:
+  nsIThread* mThread;
+
+  void SetInWait() {
+    MutexAutoLock lock(mWaitLock);
+    mInWait = true;
+  }
+
+  void ClearInWait() {
+    MutexAutoLock lock(mWaitLock);
+    mInWait = false;
+  }
+
+  bool GetInWait() {
+    MutexAutoLock lock(mWaitLock);
+    return mInWait;
+  }
+
+private:
+  ~MessagePumpForNonMainUIThreads()
+  {
+  }
+
+  bool mInWait;
+  mozilla::Mutex mWaitLock;
+};
+#endif // defined(XP_WIN)
+
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_MESSAGEPUMP_H__ */
--- a/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
+++ b/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
@@ -50,22 +50,22 @@ class ThreadWrapper {
   // obj         Object associated with the thread. Passed in the callback
   //             function.
   // prio        Thread priority. May require root/admin rights.
   // thread_name  NULL terminated thread name, will be visable in the Windows
   //             debugger.
   static ThreadWrapper* CreateThread(ThreadRunFunction func,
                                      ThreadObj obj,
                                      ThreadPriority prio = kNormalPriority,
-                                     const char* thread_name = 0);
+                                     const char* thread_name = NULL);
 
   static ThreadWrapper* CreateUIThread(ThreadRunFunction func,
                                        ThreadObj obj,
                                        ThreadPriority prio = kNormalPriority,
-                                       const char* thread_name = 0);
+                                       const char* thread_name = NULL);
 
   // Get the current thread's kernel thread ID.
   static uint32_t GetThreadId();
 
   // Non blocking termination of the spawned thread. Note that it is not safe
   // to delete this class until the spawned thread has been reclaimed.
   virtual void SetNotAlive() = 0;
 
--- a/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
+++ b/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
@@ -19,17 +19,17 @@
 #include "webrtc/system_wrappers/source/set_thread_name_win.h"
 
 namespace webrtc {
 
 // For use in ThreadWindowsUI callbacks
 static UINT static_reg_windows_msg = RegisterWindowMessageW(L"WebrtcWindowsUIThreadEvent");
 // timer id used in delayed callbacks
 static const UINT_PTR kTimerId = 1;
-static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr"; 
+static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
 static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
 
 ThreadWindows::ThreadWindows(ThreadRunFunction func, ThreadObj obj,
                              ThreadPriority prio, const char* thread_name)
     : ThreadWrapper(),
       run_function_(func),
       obj_(obj),
       alive_(false),
@@ -251,29 +251,29 @@ bool ThreadWindowsUI::Stop() {
   } else {
     return false;
   }
 }
 
 bool ThreadWindowsUI::InternalInit() {
   // Create an event window for use in generating callbacks to capture
   // objects.
-  if (hwnd_ == nullptr) {
+  if (hwnd_ == NULL) {
     WNDCLASSW wc;
-    HMODULE hModule = GetModuleHandle(nullptr);
+    HMODULE hModule = GetModuleHandle(NULL);
     if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
       ZeroMemory(&wc, sizeof(WNDCLASSW));
       wc.hInstance = hModule;
       wc.lpfnWndProc = EventWindowProc;
       wc.lpszClassName = kThreadWindow;
       RegisterClassW(&wc);
     }
     hwnd_ = CreateWindowW(kThreadWindow, L"",
                           0, 0, 0, 0, 0,
-                          nullptr, nullptr, hModule, nullptr);
+                          NULL, NULL, hModule, NULL);
     assert(hwnd_);
     SetPropW(hwnd_, kThisProperty, this);
   }
   return !!hwnd_;
 }
 
 void ThreadWindowsUI::RequestCallback() {
   assert(hwnd_);
@@ -281,17 +281,17 @@ void ThreadWindowsUI::RequestCallback() 
   PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
 }
 
 bool ThreadWindowsUI::RequestCallbackTimer(unsigned int milliseconds) {
   assert(hwnd_);
   if (timerid_) {
     KillTimer(hwnd_, timerid_);
   }
-  timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, nullptr);
+  timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
   return !!timerid_;
 }
 
 void ThreadWindowsUI::Run() {
   assert(hwnd_);
 
   alive_ = true;
   dead_ = false;