ipc/glue/MessagePump.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "MessagePump.h"
     7 #include "nsIRunnable.h"
     8 #include "nsIThread.h"
     9 #include "nsITimer.h"
    11 #include "base/basictypes.h"
    12 #include "base/logging.h"
    13 #include "base/scoped_nsautorelease_pool.h"
    14 #include "mozilla/Assertions.h"
    15 #include "mozilla/DebugOnly.h"
    16 #include "nsComponentManagerUtils.h"
    17 #include "nsDebug.h"
    18 #include "nsServiceManagerUtils.h"
    19 #include "nsString.h"
    20 #include "nsThreadUtils.h"
    21 #include "nsTimerImpl.h"
    22 #include "nsXULAppAPI.h"
    23 #include "prthread.h"
    25 #ifdef MOZ_WIDGET_ANDROID
    26 #include "AndroidBridge.h"
    27 #endif
    29 #ifdef MOZ_NUWA_PROCESS
    30 #include "ipc/Nuwa.h"
    31 #endif
    33 using base::TimeTicks;
    34 using namespace mozilla::ipc;
    36 NS_DEFINE_NAMED_CID(NS_TIMER_CID);
    38 static mozilla::DebugOnly<MessagePump::Delegate*> gFirstDelegate;
    40 namespace mozilla {
    41 namespace ipc {
    43 class DoWorkRunnable MOZ_FINAL : public nsIRunnable,
    44                                  public nsITimerCallback
    45 {
    46 public:
    47   DoWorkRunnable(MessagePump* aPump)
    48   : mPump(aPump)
    49   {
    50     MOZ_ASSERT(aPump);
    51   }
    53   NS_DECL_THREADSAFE_ISUPPORTS
    54   NS_DECL_NSIRUNNABLE
    55   NS_DECL_NSITIMERCALLBACK
    57 private:
    58   ~DoWorkRunnable()
    59   { }
    61   MessagePump* mPump;
    62 };
    64 } /* namespace ipc */
    65 } /* namespace mozilla */
    67 MessagePump::MessagePump()
    68 : mThread(nullptr)
    69 {
    70   mDoWorkEvent = new DoWorkRunnable(this);
    71 }
    73 MessagePump::~MessagePump()
    74 {
    75 }
    77 void
    78 MessagePump::Run(MessagePump::Delegate* aDelegate)
    79 {
    80   MOZ_ASSERT(keep_running_);
    81   MOZ_ASSERT(NS_IsMainThread(),
    82              "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
    84   mThread = NS_GetCurrentThread();
    85   MOZ_ASSERT(mThread);
    87   mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
    88   MOZ_ASSERT(mDelayedWorkTimer);
    90   base::ScopedNSAutoreleasePool autoReleasePool;
    92   for (;;) {
    93     autoReleasePool.Recycle();
    95     bool did_work = NS_ProcessNextEvent(mThread, false) ? true : false;
    96     if (!keep_running_)
    97       break;
    99     // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
   100     // here.  To ensure that MessageLoop tasks and XPCOM events have
   101     // equal priority, we sensitively rely on processing exactly one
   102     // Task per DoWorkRunnable XPCOM event.
   104 #ifdef MOZ_WIDGET_ANDROID
   105     // This processes messages in the Android Looper. Note that we only
   106     // get here if the normal Gecko event loop has been awoken above.
   107     // Bug 750713
   108     if (MOZ_LIKELY(AndroidBridge::HasEnv())) {
   109         did_work |= mozilla::widget::android::GeckoAppShell::PumpMessageLoop();
   110     }
   111 #endif
   113     did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
   115 if (did_work && delayed_work_time_.is_null()
   116 #ifdef MOZ_NUWA_PROCESS
   117     && (!IsNuwaReady() || !IsNuwaProcess())
   118 #endif
   119    )
   120       mDelayedWorkTimer->Cancel();
   122     if (!keep_running_)
   123       break;
   125     if (did_work)
   126       continue;
   128     did_work = aDelegate->DoIdleWork();
   129     if (!keep_running_)
   130       break;
   132     if (did_work)
   133       continue;
   135     // This will either sleep or process an event.
   136     NS_ProcessNextEvent(mThread, true);
   137   }
   139 #ifdef MOZ_NUWA_PROCESS
   140   if (!IsNuwaReady() || !IsNuwaProcess())
   141 #endif
   142     mDelayedWorkTimer->Cancel();
   144   keep_running_ = true;
   145 }
   147 void
   148 MessagePump::ScheduleWork()
   149 {
   150   // Make sure the event loop wakes up.
   151   if (mThread) {
   152     mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
   153   }
   154   else {
   155     // Some things (like xpcshell) don't use the app shell and so Run hasn't
   156     // been called. We still need to wake up the main thread.
   157     NS_DispatchToMainThread(mDoWorkEvent, NS_DISPATCH_NORMAL);
   158   }
   159   event_.Signal();
   160 }
   162 void
   163 MessagePump::ScheduleWorkForNestedLoop()
   164 {
   165   // This method is called when our MessageLoop has just allowed
   166   // nested tasks.  In our setup, whenever that happens we know that
   167   // DoWork() will be called "soon", so there's no need to pay the
   168   // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
   169 }
   171 void
   172 MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
   173 {
   174 #ifdef MOZ_NUWA_PROCESS
   175   if (IsNuwaReady() && IsNuwaProcess())
   176     return;
   177 #endif
   179   if (!mDelayedWorkTimer) {
   180     mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
   181     if (!mDelayedWorkTimer) {
   182         // Called before XPCOM has started up? We can't do this correctly.
   183         NS_WARNING("Delayed task might not run!");
   184         delayed_work_time_ = aDelayedTime;
   185         return;
   186     }
   187   }
   189   if (!delayed_work_time_.is_null()) {
   190     mDelayedWorkTimer->Cancel();
   191   }
   193   delayed_work_time_ = aDelayedTime;
   195   // TimeDelta's constructor initializes to 0
   196   base::TimeDelta delay;
   197   if (aDelayedTime > base::TimeTicks::Now())
   198     delay = aDelayedTime - base::TimeTicks::Now();
   200   uint32_t delayMS = uint32_t(delay.InMilliseconds());
   201   mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
   202                                       nsITimer::TYPE_ONE_SHOT);
   203 }
   205 void
   206 MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
   207 {
   208   aDelegate->DoDelayedWork(&delayed_work_time_);
   209   if (!delayed_work_time_.is_null()) {
   210     ScheduleDelayedWork(delayed_work_time_);
   211   }
   212 }
   214 NS_IMPL_ISUPPORTS(DoWorkRunnable, nsIRunnable, nsITimerCallback)
   216 NS_IMETHODIMP
   217 DoWorkRunnable::Run()
   218 {
   219   MessageLoop* loop = MessageLoop::current();
   220   MOZ_ASSERT(loop);
   222   bool nestableTasksAllowed = loop->NestableTasksAllowed();
   224   // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
   225   // always dispatch DoWork() below from what looks to MessageLoop like a nested
   226   // context.  So we unconditionally allow nesting here.
   227   loop->SetNestableTasksAllowed(true);
   228   loop->DoWork();
   229   loop->SetNestableTasksAllowed(nestableTasksAllowed);
   231   return NS_OK;
   232 }
   234 NS_IMETHODIMP
   235 DoWorkRunnable::Notify(nsITimer* aTimer)
   236 {
   237   MessageLoop* loop = MessageLoop::current();
   238   MOZ_ASSERT(loop);
   240   mPump->DoDelayedWork(loop);
   242   return NS_OK;
   243 }
   245 void
   246 MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
   247 {
   248   if (mFirstRun) {
   249     MOZ_ASSERT(aDelegate && !gFirstDelegate);
   250     gFirstDelegate = aDelegate;
   252     mFirstRun = false;
   253     if (NS_FAILED(XRE_RunAppShell())) {
   254         NS_WARNING("Failed to run app shell?!");
   255     }
   257     MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
   258     gFirstDelegate = nullptr;
   260     return;
   261   }
   263   MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
   265   // We can get to this point in startup with Tasks in our loop's
   266   // incoming_queue_ or pending_queue_, but without a matching
   267   // DoWorkRunnable().  In MessagePump::Run() above, we sensitively
   268   // depend on *not* directly calling delegate->DoWork(), because that
   269   // prioritizes Tasks above XPCOM events.  However, from this point
   270   // forward, any Task posted to our loop is guaranteed to have a
   271   // DoWorkRunnable enqueued for it.
   272   //
   273   // So we just flush the pending work here and move on.
   274   MessageLoop* loop = MessageLoop::current();
   275   bool nestableTasksAllowed = loop->NestableTasksAllowed();
   276   loop->SetNestableTasksAllowed(true);
   278   while (aDelegate->DoWork());
   280   loop->SetNestableTasksAllowed(nestableTasksAllowed);
   282   // Really run.
   283   mozilla::ipc::MessagePump::Run(aDelegate);
   284 }
   286 void
   287 MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate)
   288 {
   289   MOZ_ASSERT(keep_running_);
   290   MOZ_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
   292   mThread = NS_GetCurrentThread();
   293   MOZ_ASSERT(mThread);
   295   mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
   296   MOZ_ASSERT(mDelayedWorkTimer);
   298   if (NS_FAILED(mDelayedWorkTimer->SetTarget(mThread))) {
   299     MOZ_CRASH("Failed to set timer target!");
   300   }
   302   base::ScopedNSAutoreleasePool autoReleasePool;
   304   for (;;) {
   305     autoReleasePool.Recycle();
   307     bool didWork = NS_ProcessNextEvent(mThread, false) ? true : false;
   308     if (!keep_running_) {
   309       break;
   310     }
   312     didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
   314     if (didWork && delayed_work_time_.is_null()) {
   315       mDelayedWorkTimer->Cancel();
   316     }
   318     if (!keep_running_) {
   319       break;
   320     }
   322     if (didWork) {
   323       continue;
   324     }
   326     didWork = aDelegate->DoIdleWork();
   327     if (!keep_running_) {
   328       break;
   329     }
   331     if (didWork) {
   332       continue;
   333     }
   335     // This will either sleep or process an event.
   336     NS_ProcessNextEvent(mThread, true);
   337   }
   339   mDelayedWorkTimer->Cancel();
   341   keep_running_ = true;
   342 }

mercurial