Fix PostDelayedTask in the message loop
authorBen Turner <bent.mozilla@gmail.com>
Mon, 23 Nov 2009 16:01:12 -0500
changeset 36107 0ce2fd2badaa7491f82f1d1757f88bbc00954718
parent 36106 db9989871e18ed7ab9d4f2854961cc08f219e061
child 36108 133d4a382af1cc2d7ab028fb93b32951dc03853b
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone1.9.3a1pre
Fix PostDelayedTask in the message loop
ipc/chromium/src/base/message_loop.h
ipc/glue/MessagePump.cpp
ipc/glue/MessagePump.h
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -22,17 +22,23 @@
 // We need this to declare base::MessagePumpWin::Dispatcher, which we should
 // really just eliminate.
 #include "base/message_pump_win.h"
 #elif defined(OS_POSIX)
 #include "base/message_pump_libevent.h"
 #endif
 
 #ifdef CHROMIUM_MOZILLA_BUILD
+namespace mozilla {
+namespace ipc {
+
 class DoWorkRunnable;
+
+} /* namespace ipc */
+} /* namespace mozilla */
 #endif
 
 // A MessageLoop is used to process events for a particular thread.  There is
 // at most one MessageLoop instance per thread.
 //
 // Events include at a minimum Task instances submitted to PostTask or those
 // managed by TimerManager.  Depending on the type of message pump used by the
 // MessageLoop other events such as UI messages may be processed.  On Windows
@@ -58,17 +64,17 @@ class DoWorkRunnable;
 //   // Process hr  (the result returned by DoDragDrop().
 //
 // Please be SURE your task is reentrant (nestable) and all global variables
 // are stable and accessible before calling SetNestableTasksAllowed(true).
 //
 class MessageLoop : public base::MessagePump::Delegate {
 
 #ifdef CHROMIUM_MOZILLA_BUILD
-  friend class DoWorkRunnable;
+  friend class mozilla::ipc::DoWorkRunnable;
 #endif
 
 public:
   static void EnableHistogrammer(bool enable_histogrammer);
 
   // A DestructionObserver is notified when the current MessageLoop is being
   // destroyed.  These obsevers are notified prior to MessageLoop::current()
   // being changed to return NULL.  This gives interested parties the chance to
--- a/ipc/glue/MessagePump.cpp
+++ b/ipc/glue/MessagePump.cpp
@@ -31,172 +31,180 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "MessagePump.h"
 
-#include "nsIThread.h"
-#include "nsITimer.h"
-
 #include "nsComponentManagerUtils.h"
-#include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringGlue.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "pratom.h"
 #include "prthread.h"
 
 #include "base/logging.h"
 #include "base/scoped_nsautorelease_pool.h"
 
+using mozilla::ipc::DoWorkRunnable;
 using mozilla::ipc::MessagePump;
 using mozilla::ipc::MessagePumpForChildProcess;
+using base::Time;
 
 namespace {
 
 bool gRunningSetNestableTasksAllowed = false;
 
-void
-TimerCallback(nsITimer* aTimer,
-              void* aClosure)
-{
-  MessagePump* messagePump = reinterpret_cast<MessagePump*>(aClosure);
-  messagePump->ScheduleWork();
-}
-
 } /* anonymous namespace */
 
-class DoWorkRunnable : public nsRunnable
+NS_IMPL_THREADSAFE_ISUPPORTS2(DoWorkRunnable, nsIRunnable, nsITimerCallback)
+
+NS_IMETHODIMP
+DoWorkRunnable::Run()
 {
-public:
-  NS_IMETHOD Run() {
-    MessageLoop* loop = MessageLoop::current();
-    NS_ASSERTION(loop, "Shouldn't be null!");
-    if (loop) {
-      bool nestableTasksAllowed = loop->NestableTasksAllowed();
+  MessageLoop* loop = MessageLoop::current();
+  NS_ASSERTION(loop, "Shouldn't be null!");
+  if (loop) {
+    bool nestableTasksAllowed = loop->NestableTasksAllowed();
+
+    gRunningSetNestableTasksAllowed = true;
+    loop->SetNestableTasksAllowed(true);
+    gRunningSetNestableTasksAllowed = false;
+
+    loop->DoWork();
 
-      gRunningSetNestableTasksAllowed = true;
-      loop->SetNestableTasksAllowed(true);
-      gRunningSetNestableTasksAllowed = false;
-
-      loop->DoWork();
+    gRunningSetNestableTasksAllowed = true;
+    loop->SetNestableTasksAllowed(nestableTasksAllowed);
+    gRunningSetNestableTasksAllowed = false;
+  }
+  return NS_OK;
+}
 
-      gRunningSetNestableTasksAllowed = true;
-      loop->SetNestableTasksAllowed(nestableTasksAllowed);
-      gRunningSetNestableTasksAllowed = false;
-    }
-    return NS_OK;
+NS_IMETHODIMP
+DoWorkRunnable::Notify(nsITimer* aTimer)
+{
+  MessageLoop* loop = MessageLoop::current();
+  NS_ASSERTION(loop, "Shouldn't be null!");
+  if (loop) {
+    mPump->DoDelayedWork(loop);
   }
-};
+  return NS_OK;
+}
 
 MessagePump::MessagePump()
 : mThread(nsnull)
 {
-  mDummyEvent = new DoWorkRunnable();
-  // I'm tired of adding OOM checks.
-  NS_ADDREF(mDummyEvent);
-}
-
-MessagePump::~MessagePump()
-{
-  NS_RELEASE(mDummyEvent);
+  mDoWorkEvent = new DoWorkRunnable(this);
 }
 
 void
 MessagePump::Run(MessagePump::Delegate* aDelegate)
 {
   NS_ASSERTION(keep_running_, "Quit must have been called outside of Run!");
-
-  NS_ASSERTION(NS_IsMainThread(),
-               "This should only ever happen on Gecko's main thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Called Run on the wrong thread!");
 
   mThread = NS_GetCurrentThread();
   NS_ASSERTION(mThread, "This should never be null!");
 
-  nsCOMPtr<nsITimer> timer(do_CreateInstance(NS_TIMER_CONTRACTID));
-  NS_ASSERTION(timer, "Failed to create timer!");
+  mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  NS_ASSERTION(mDelayedWorkTimer, "Failed to create timer!");
 
   base::ScopedNSAutoreleasePool autoReleasePool;
 
   for (;;) {
     autoReleasePool.Recycle();
-    timer->Cancel();
 
     bool did_work = NS_ProcessNextEvent(mThread, PR_FALSE) ? true : false;
     if (!keep_running_)
       break;
 
     did_work |= aDelegate->DoWork();
     if (!keep_running_)
       break;
 
     did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
+
+    if (did_work && delayed_work_time_.is_null())
+      mDelayedWorkTimer->Cancel();
+
     if (!keep_running_)
       break;
 
     if (did_work)
       continue;
 
     did_work = aDelegate->DoIdleWork();
     if (!keep_running_)
       break;
 
-    if (did_work)
-      continue;
-
-    if (delayed_work_time_.is_null()) {
-      // This will sleep or process native events.
-      NS_ProcessNextEvent(mThread, PR_TRUE);
-      continue;
-    }
-
-    base::TimeDelta delay = delayed_work_time_ - base::Time::Now();
-    if (delay > base::TimeDelta()) {
-      PRUint32 delayMS = PRUint32(delay.InMilliseconds());
-      timer->InitWithFuncCallback(TimerCallback, this, delayMS,
-                                  nsITimer::TYPE_ONE_SHOT);
-      // This will sleep or process native events. The timer should wake us up
-      // if nothing else does.
-      NS_ProcessNextEvent(mThread, PR_TRUE);
-      continue;
-    }
-
-    // It looks like delayed_work_time_ indicates a time in the past, so we
-    // need to call DoDelayedWork now.
-    delayed_work_time_ = base::Time();
+    // This will either sleep or process an event.
+    NS_ProcessNextEvent(mThread, PR_TRUE);
   }
 
-  timer->Cancel();
+  mDelayedWorkTimer->Cancel();
 
   keep_running_ = true;
 }
 
 void
 MessagePump::ScheduleWork()
 {
   if (gRunningSetNestableTasksAllowed) {
     return;
   }
 
   // Make sure the event loop wakes up.
   if (mThread) {
-    mThread->Dispatch(mDummyEvent, NS_DISPATCH_NORMAL);
+    mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
   }
   else {
     // Some things (like xpcshell) don't use the app shell and so Run hasn't
     // been called. We still need to wake up the main thread.
-    NS_DispatchToMainThread(mDummyEvent, NS_DISPATCH_NORMAL);
+    NS_DispatchToMainThread(mDoWorkEvent, NS_DISPATCH_NORMAL);
   }
   event_.Signal();
 }
 
+void
+MessagePump::ScheduleDelayedWork(const base::Time& aDelayedTime)
+{
+  if (!mDelayedWorkTimer) {
+    mDelayedWorkTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    if (!mDelayedWorkTimer) {
+        // Called before XPCOM has started up? We can't do this correctly.
+        NS_WARNING("Delayed task might not run!");
+        delayed_work_time_ = aDelayedTime;
+        return;
+    }
+  }
+
+  if (!delayed_work_time_.is_null()) {
+    mDelayedWorkTimer->Cancel();
+  }
+
+  delayed_work_time_ = aDelayedTime;
+
+  base::TimeDelta delay = aDelayedTime - base::Time::Now();
+  PRUint32 delayMS = PRUint32(delay.InMilliseconds());
+  mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
+                                      nsITimer::TYPE_ONE_SHOT);
+}
+
+void
+MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
+{
+  aDelegate->DoDelayedWork(&delayed_work_time_);
+  if (!delayed_work_time_.is_null()) {
+    ScheduleDelayedWork(delayed_work_time_);
+  }
+}
+
 #ifdef DEBUG
 namespace {
 MessagePump::Delegate* gFirstDelegate = nsnull;
 }
 #endif
 
 void
 MessagePumpForChildProcess::Run(MessagePump::Delegate* aDelegate)
--- a/ipc/glue/MessagePump.h
+++ b/ipc/glue/MessagePump.h
@@ -32,38 +32,62 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __IPC_GLUE_MESSAGEPUMP_H__
 #define __IPC_GLUE_MESSAGEPUMP_H__
 
+#include "base/basictypes.h"
 #include "base/message_pump_default.h"
+#include "base/time.h"
 
-#include "prtypes.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 
-class nsIRunnable;
-class nsIThread;
+#include "nsIRunnable.h"
+#include "nsIThread.h"
+#include "nsITimer.h"
 
 namespace mozilla {
 namespace ipc {
 
-class MessagePump : public base::MessagePumpDefault
+class MessagePump;
+
+class DoWorkRunnable : public nsIRunnable,
+                       public nsITimerCallback
 {
 public:
+  DoWorkRunnable(MessagePump* aPump)
+  : mPump(aPump) { }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSITIMERCALLBACK
+
+private:
+  MessagePump* mPump;
+};
+
+class MessagePump : public base::MessagePumpDefault
+{
+
+public:
   MessagePump();
-  ~MessagePump();
 
   virtual void Run(base::MessagePump::Delegate* aDelegate);
   virtual void ScheduleWork();
+  virtual void ScheduleDelayedWork(const base::Time& delayed_work_time);
+
+  void DoDelayedWork(base::MessagePump::Delegate* aDelegate);
 
 private:
-  nsIRunnable* mDummyEvent;
+  nsRefPtr<DoWorkRunnable> mDoWorkEvent;
+  nsCOMPtr<nsITimer> mDelayedWorkTimer;
 
   // Weak!
   nsIThread* mThread;
 };
 
 class MessagePumpForChildProcess : public MessagePump
 {
 public: