Bug 939182 - Integrate the chromium MessageLoop into nsThread, r=bsmedberg.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 23 Oct 2013 05:01:24 -0700
changeset 159850 fa74710fdeefbd146f716a5a432344f82d928a81
parent 159849 1d2659a098c28049976213050c0083c65d0d175e
child 159851 072161a22749bb16a76ca78c956c6997a5295bfb
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs939182
milestone29.0a1
Bug 939182 - Integrate the chromium MessageLoop into nsThread, r=bsmedberg.
ipc/chromium/src/base/message_loop.cc
ipc/chromium/src/base/message_loop.h
ipc/glue/MessagePump.cpp
ipc/glue/MessagePump.h
xpcom/threads/moz.build
xpcom/threads/nsThread.cpp
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -113,16 +113,20 @@ MessageLoop::MessageLoop(Type type)
     // 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) {
+    pump_ = new mozilla::ipc::MessagePumpForNonMainThreads();
+    return;
+  }
 
 #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();
   } else {
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -198,22 +198,27 @@ public:
   // TYPE_MOZILLA_CHILD
   //   This type of ML is used in Mozilla child processes which initialize
   //   XPCOM and use the gecko event loop.
   //
   // 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.
+  //
   enum Type {
     TYPE_DEFAULT,
     TYPE_UI,
     TYPE_IO,
     TYPE_MOZILLA_CHILD,
-    TYPE_MOZILLA_UI
+    TYPE_MOZILLA_UI,
+    TYPE_MOZILLA_NONMAINTHREAD
   };
 
   // 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/glue/MessagePump.cpp
+++ b/ipc/glue/MessagePump.cpp
@@ -70,17 +70,18 @@ MessagePump::MessagePump()
 MessagePump::~MessagePump()
 {
 }
 
 void
 MessagePump::Run(MessagePump::Delegate* aDelegate)
 {
   MOZ_ASSERT(keep_running_);
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
 
   mThread = NS_GetCurrentThread();
   MOZ_ASSERT(mThread);
 
   mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   MOZ_ASSERT(mDelayedWorkTimer);
 
   base::ScopedNSAutoreleasePool autoReleasePool;
@@ -271,8 +272,62 @@ MessagePumpForChildProcess::Run(base::Me
 
   while (aDelegate->DoWork());
 
   loop->SetNestableTasksAllowed(nestableTasksAllowed);
 
   // Really run.
   mozilla::ipc::MessagePump::Run(aDelegate);
 }
+
+void
+MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate)
+{
+  MOZ_ASSERT(keep_running_);
+  MOZ_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
+
+  mThread = NS_GetCurrentThread();
+  MOZ_ASSERT(mThread);
+
+  mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  MOZ_ASSERT(mDelayedWorkTimer);
+
+  if (NS_FAILED(mDelayedWorkTimer->SetTarget(mThread))) {
+    MOZ_CRASH("Failed to set timer target!");
+  }
+
+  for (;;) {
+    bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false;
+    if (!keep_running_) {
+      break;
+    }
+
+    didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
+
+    if (didWork && delayed_work_time_.is_null()) {
+      mDelayedWorkTimer->Cancel();
+    }
+
+    if (!keep_running_) {
+      break;
+    }
+
+    if (didWork) {
+      continue;
+    }
+
+    didWork = aDelegate->DoIdleWork();
+    if (!keep_running_) {
+      break;
+    }
+
+    if (didWork) {
+      continue;
+    }
+
+    // This will either sleep or process an event.
+    NS_ProcessNextEvent(mThread, true);
+  }
+
+  mDelayedWorkTimer->Cancel();
+
+  keep_running_ = true;
+}
--- a/ipc/glue/MessagePump.h
+++ b/ipc/glue/MessagePump.h
@@ -71,12 +71,25 @@ public:
 
 private:
   ~MessagePumpForChildProcess()
   { }
 
   bool mFirstRun;
 };
 
+class MessagePumpForNonMainThreads MOZ_FINAL : public MessagePump
+{
+public:
+  MessagePumpForNonMainThreads()
+  { }
+
+  virtual void Run(base::MessagePump::Delegate* aDelegate) MOZ_OVERRIDE;
+
+private:
+  ~MessagePumpForNonMainThreads()
+  { }
+};
+
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_MESSAGEPUMP_H__ */
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -52,8 +52,10 @@ UNIFIED_SOURCES += [
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '../build',
 ]
 
 FINAL_LIBRARY = 'xpcom_core'
+
+include('/ipc/chromium/chromium-config.mozbuild')
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -1,17 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsThread.h"
+
+#include "base/message_loop.h"
+
+// Chromium's logging can sometimes leak through...
+#ifdef LOG
+#undef LOG
+#endif
+
 #include "mozilla/ReentrantMonitor.h"
 #include "nsMemoryPressure.h"
-#include "nsThread.h"
 #include "nsThreadManager.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "pratom.h"
 #include "prlog.h"
 #include "nsIObserverService.h"
@@ -209,16 +217,17 @@ private:
 // This event is responsible for setting mShutdownContext
 class nsThreadShutdownEvent : public nsRunnable {
 public:
   nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
     : mThread(thr), mShutdownContext(ctx) {
   } 
   NS_IMETHOD Run() {
     mThread->mShutdownContext = mShutdownContext;
+    MessageLoop::current()->Quit();
     return NS_OK;
   }
 private:
   nsRefPtr<nsThread>       mThread;
   nsThreadShutdownContext *mShutdownContext;
 };
 
 //-----------------------------------------------------------------------------
@@ -236,38 +245,42 @@ nsThread::ThreadFunc(void *arg)
   nsCOMPtr<nsIRunnable> event;
   if (!self->GetEvent(true, getter_AddRefs(event))) {
     NS_WARNING("failed waiting for thread startup event");
     return;
   }
   event->Run();  // unblocks nsThread::Init
   event = nullptr;
 
-  // Now, process incoming events...
-  while (!self->ShuttingDown())
-    NS_ProcessNextEvent(self);
+  { // Scope for MessageLoop.
+    nsAutoPtr<MessageLoop> loop(
+      new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD));
+
+    // Now, process incoming events...
+    loop->Run();
 
-  // Do NS_ProcessPendingEvents but with special handling to set
-  // mEventsAreDoomed atomically with the removal of the last event. The key
-  // invariant here is that we will never permit PutEvent to succeed if the
-  // event would be left in the queue after our final call to
-  // NS_ProcessPendingEvents.
-  while (true) {
-    {
-      MutexAutoLock lock(self->mLock);
-      if (!self->mEvents.HasPendingEvent()) {
-        // No events in the queue, so we will stop now. Don't let any more
-        // events be added, since they won't be processed. It is critical
-        // that no PutEvent can occur between testing that the event queue is
-        // empty and setting mEventsAreDoomed!
-        self->mEventsAreDoomed = true;
-        break;
+    // Do NS_ProcessPendingEvents but with special handling to set
+    // mEventsAreDoomed atomically with the removal of the last event. The key
+    // invariant here is that we will never permit PutEvent to succeed if the
+    // event would be left in the queue after our final call to
+    // NS_ProcessPendingEvents.
+    while (true) {
+      {
+        MutexAutoLock lock(self->mLock);
+        if (!self->mEvents.HasPendingEvent()) {
+          // No events in the queue, so we will stop now. Don't let any more
+          // events be added, since they won't be processed. It is critical
+          // that no PutEvent can occur between testing that the event queue is
+          // empty and setting mEventsAreDoomed!
+          self->mEventsAreDoomed = true;
+          break;
+        }
       }
+      NS_ProcessPendingEvents(self);
     }
-    NS_ProcessPendingEvents(self);
   }
 
   // Inform the threadmanager that this thread is going away
   nsThreadManager::get()->UnregisterCurrentThread(self);
 
   // Dispatch shutdown ACK
   event = new nsThreadShutdownAckEvent(self->mShutdownContext);
   self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);