LCOV - code coverage report
Current view: top level - ipc/glue - MessagePump.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 85 148 57.4 %
Date: 2017-07-14 16:53:18 Functions: 11 20 55.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "MessagePump.h"
       8             : 
       9             : #include "nsIRunnable.h"
      10             : #include "nsIThread.h"
      11             : #include "nsITimer.h"
      12             : #include "nsICancelableRunnable.h"
      13             : 
      14             : #include "base/basictypes.h"
      15             : #include "base/logging.h"
      16             : #include "base/scoped_nsautorelease_pool.h"
      17             : #include "mozilla/Assertions.h"
      18             : #include "mozilla/DebugOnly.h"
      19             : #include "nsComponentManagerUtils.h"
      20             : #include "nsDebug.h"
      21             : #include "nsServiceManagerUtils.h"
      22             : #include "nsString.h"
      23             : #include "nsThreadUtils.h"
      24             : #include "nsTimerImpl.h"
      25             : #include "nsXULAppAPI.h"
      26             : #include "prthread.h"
      27             : 
      28             : using base::TimeTicks;
      29             : using namespace mozilla::ipc;
      30             : 
      31             : NS_DEFINE_NAMED_CID(NS_TIMER_CID);
      32             : 
      33             : #ifdef DEBUG
      34             : static MessagePump::Delegate* gFirstDelegate;
      35             : #endif
      36             : 
      37             : namespace mozilla {
      38             : namespace ipc {
      39             : 
      40             : class DoWorkRunnable final : public CancelableRunnable,
      41             :                              public nsITimerCallback
      42             : {
      43             : public:
      44          59 :   explicit DoWorkRunnable(MessagePump* aPump)
      45          59 :     : CancelableRunnable("ipc::DoWorkRunnable")
      46          59 :     , mPump(aPump)
      47             :   {
      48          59 :     MOZ_ASSERT(aPump);
      49          59 :   }
      50             : 
      51             :   NS_DECL_ISUPPORTS_INHERITED
      52             :   NS_DECL_NSIRUNNABLE
      53             :   NS_DECL_NSITIMERCALLBACK
      54             :   nsresult Cancel() override;
      55             : 
      56             : private:
      57           2 :   ~DoWorkRunnable()
      58           3 :   { }
      59             : 
      60             :   MessagePump* mPump;
      61             :   // DoWorkRunnable is designed as a stateless singleton.  Do not add stateful
      62             :   // members here!
      63             : };
      64             : 
      65             : } /* namespace ipc */
      66             : } /* namespace mozilla */
      67             : 
      68          59 : MessagePump::MessagePump(nsIThread* aThread)
      69          59 : : mThread(aThread)
      70             : {
      71          59 :   mDoWorkEvent = new DoWorkRunnable(this);
      72          59 : }
      73             : 
      74           1 : MessagePump::~MessagePump()
      75             : {
      76           1 : }
      77             : 
      78             : void
      79           3 : MessagePump::Run(MessagePump::Delegate* aDelegate)
      80             : {
      81           3 :   MOZ_ASSERT(keep_running_);
      82           3 :   MOZ_RELEASE_ASSERT(NS_IsMainThread(),
      83             :                      "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
      84           3 :   MOZ_RELEASE_ASSERT(!mThread);
      85             : 
      86           3 :   nsIThread* thisThread = NS_GetCurrentThread();
      87           3 :   MOZ_ASSERT(thisThread);
      88             : 
      89           3 :   mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
      90           3 :   MOZ_ASSERT(mDelayedWorkTimer);
      91             : 
      92           3 :   base::ScopedNSAutoreleasePool autoReleasePool;
      93             : 
      94             :   for (;;) {
      95        1141 :     autoReleasePool.Recycle();
      96             : 
      97        1141 :     bool did_work = NS_ProcessNextEvent(thisThread, false) ? true : false;
      98        1139 :     if (!keep_running_)
      99           0 :       break;
     100             : 
     101             :     // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
     102             :     // here.  To ensure that MessageLoop tasks and XPCOM events have
     103             :     // equal priority, we sensitively rely on processing exactly one
     104             :     // Task per DoWorkRunnable XPCOM event.
     105             : 
     106        1139 :     did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
     107             : 
     108        1139 : if (did_work && delayed_work_time_.is_null())
     109        1066 :       mDelayedWorkTimer->Cancel();
     110             : 
     111        1139 :     if (!keep_running_)
     112           0 :       break;
     113             : 
     114        1139 :     if (did_work)
     115        1066 :       continue;
     116             : 
     117          73 :     did_work = aDelegate->DoIdleWork();
     118          73 :     if (!keep_running_)
     119           0 :       break;
     120             : 
     121          73 :     if (did_work)
     122           0 :       continue;
     123             : 
     124             :     // This will either sleep or process an event.
     125          73 :     NS_ProcessNextEvent(thisThread, true);
     126        1138 :   }
     127             : 
     128           0 :     mDelayedWorkTimer->Cancel();
     129             : 
     130           0 :   keep_running_ = true;
     131           0 : }
     132             : 
     133             : void
     134           0 : MessagePump::ScheduleWork()
     135             : {
     136             :   // Make sure the event loop wakes up.
     137           0 :   if (mThread) {
     138           0 :     mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
     139             :   } else {
     140             :     // Some things (like xpcshell) don't use the app shell and so Run hasn't
     141             :     // been called. We still need to wake up the main thread.
     142           0 :     NS_DispatchToMainThread(mDoWorkEvent);
     143             :   }
     144           0 :   event_.Signal();
     145           0 : }
     146             : 
     147             : void
     148           0 : MessagePump::ScheduleWorkForNestedLoop()
     149             : {
     150             :   // This method is called when our MessageLoop has just allowed
     151             :   // nested tasks.  In our setup, whenever that happens we know that
     152             :   // DoWork() will be called "soon", so there's no need to pay the
     153             :   // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
     154           0 : }
     155             : 
     156             : void
     157           0 : MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
     158             : {
     159             :   // To avoid racing on mDelayedWorkTimer, we need to be on the same thread as
     160             :   // ::Run().
     161           0 :   MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mThread ||
     162             :                      (!mThread && NS_IsMainThread()));
     163             : 
     164           0 :   if (!mDelayedWorkTimer) {
     165           0 :     mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
     166           0 :     if (!mDelayedWorkTimer) {
     167             :         // Called before XPCOM has started up? We can't do this correctly.
     168           0 :         NS_WARNING("Delayed task might not run!");
     169           0 :         delayed_work_time_ = aDelayedTime;
     170           0 :         return;
     171             :     }
     172             :   }
     173             : 
     174           0 :   if (!delayed_work_time_.is_null()) {
     175           0 :     mDelayedWorkTimer->Cancel();
     176             :   }
     177             : 
     178           0 :   delayed_work_time_ = aDelayedTime;
     179             : 
     180             :   // TimeDelta's constructor initializes to 0
     181           0 :   base::TimeDelta delay;
     182           0 :   if (aDelayedTime > base::TimeTicks::Now())
     183           0 :     delay = aDelayedTime - base::TimeTicks::Now();
     184             : 
     185           0 :   uint32_t delayMS = uint32_t(delay.InMilliseconds());
     186           0 :   mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
     187           0 :                                       nsITimer::TYPE_ONE_SHOT);
     188             : }
     189             : 
     190             : nsIEventTarget*
     191         268 : MessagePump::GetXPCOMThread()
     192             : {
     193         268 :   if (mThread) {
     194          40 :     return mThread;
     195             :   }
     196             : 
     197             :   // Main thread
     198         456 :   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     199         228 :   return mainThread;
     200             : }
     201             : 
     202             : void
     203           0 : MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
     204             : {
     205           0 :   aDelegate->DoDelayedWork(&delayed_work_time_);
     206           0 :   if (!delayed_work_time_.is_null()) {
     207           0 :     ScheduleDelayedWork(delayed_work_time_);
     208             :   }
     209           0 : }
     210             : 
     211          60 : NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable, CancelableRunnable,
     212             :                             nsITimerCallback)
     213             : 
     214             : NS_IMETHODIMP
     215           0 : DoWorkRunnable::Run()
     216             : {
     217           0 :   MessageLoop* loop = MessageLoop::current();
     218           0 :   MOZ_ASSERT(loop);
     219             : 
     220           0 :   bool nestableTasksAllowed = loop->NestableTasksAllowed();
     221             : 
     222             :   // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
     223             :   // always dispatch DoWork() below from what looks to MessageLoop like a nested
     224             :   // context.  So we unconditionally allow nesting here.
     225           0 :   loop->SetNestableTasksAllowed(true);
     226           0 :   loop->DoWork();
     227           0 :   loop->SetNestableTasksAllowed(nestableTasksAllowed);
     228             : 
     229           0 :   return NS_OK;
     230             : }
     231             : 
     232             : NS_IMETHODIMP
     233           0 : DoWorkRunnable::Notify(nsITimer* aTimer)
     234             : {
     235           0 :   MessageLoop* loop = MessageLoop::current();
     236           0 :   MOZ_ASSERT(loop);
     237             : 
     238           0 :   bool nestableTasksAllowed = loop->NestableTasksAllowed();
     239           0 :   loop->SetNestableTasksAllowed(true);
     240           0 :   mPump->DoDelayedWork(loop);
     241           0 :   loop->SetNestableTasksAllowed(nestableTasksAllowed);
     242             : 
     243           0 :   return NS_OK;
     244             : }
     245             : 
     246             : nsresult
     247           0 : DoWorkRunnable::Cancel()
     248             : {
     249             :   // Workers require cancelable runnables, but we can't really cancel cleanly
     250             :   // here.  If we don't process this runnable then we will leave something
     251             :   // unprocessed in the message_loop.  Therefore, eagerly complete our work
     252             :   // instead by immediately calling Run().  Run() should be called separately
     253             :   // after this.  Unfortunately we cannot use flags to verify this because
     254             :   // DoWorkRunnable is a stateless singleton that can be in the event queue
     255             :   // multiple times simultaneously.
     256           0 :   MOZ_ALWAYS_SUCCEEDS(Run());
     257           0 :   return NS_OK;
     258             : }
     259             : 
     260             : void
     261           4 : MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
     262             : {
     263           4 :   if (mFirstRun) {
     264           2 :     MOZ_ASSERT(aDelegate && !gFirstDelegate);
     265             : #ifdef DEBUG
     266           2 :     gFirstDelegate = aDelegate;
     267             : #endif
     268             : 
     269           2 :     mFirstRun = false;
     270           2 :     if (NS_FAILED(XRE_RunAppShell())) {
     271           0 :         NS_WARNING("Failed to run app shell?!");
     272             :     }
     273             : 
     274           0 :     MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
     275             : #ifdef DEBUG
     276           0 :     gFirstDelegate = nullptr;
     277             : #endif
     278             : 
     279           0 :     return;
     280             :   }
     281             : 
     282           2 :   MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
     283             : 
     284             :   // We can get to this point in startup with Tasks in our loop's
     285             :   // incoming_queue_ or pending_queue_, but without a matching
     286             :   // DoWorkRunnable().  In MessagePump::Run() above, we sensitively
     287             :   // depend on *not* directly calling delegate->DoWork(), because that
     288             :   // prioritizes Tasks above XPCOM events.  However, from this point
     289             :   // forward, any Task posted to our loop is guaranteed to have a
     290             :   // DoWorkRunnable enqueued for it.
     291             :   //
     292             :   // So we just flush the pending work here and move on.
     293           2 :   MessageLoop* loop = MessageLoop::current();
     294           2 :   bool nestableTasksAllowed = loop->NestableTasksAllowed();
     295           2 :   loop->SetNestableTasksAllowed(true);
     296             : 
     297           2 :   while (aDelegate->DoWork());
     298             : 
     299           2 :   loop->SetNestableTasksAllowed(nestableTasksAllowed);
     300             : 
     301             :   // Really run.
     302           2 :   mozilla::ipc::MessagePump::Run(aDelegate);
     303             : }
     304             : 
     305             : void
     306          56 : MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate)
     307             : {
     308          56 :   MOZ_ASSERT(keep_running_);
     309          56 :   MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
     310             : 
     311          56 :   nsIThread* thread = NS_GetCurrentThread();
     312          56 :   MOZ_RELEASE_ASSERT(mThread == thread);
     313             : 
     314          56 :   mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
     315          56 :   MOZ_ASSERT(mDelayedWorkTimer);
     316             : 
     317          56 :   if (NS_FAILED(mDelayedWorkTimer->SetTarget(thread))) {
     318           0 :     MOZ_CRASH("Failed to set timer target!");
     319             :   }
     320             : 
     321             :   // Chromium event notifications to be processed will be received by this
     322             :   // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that
     323             :   // were received before our thread is valid, however, will not generate
     324             :   // runnable wrappers. We must process any of these before we enter this
     325             :   // loop, or we will forever have unprocessed chromium messages in our queue.
     326             :   //
     327             :   // Note we would like to request a flush of the chromium event queue
     328             :   // using a runnable on the xpcom side, but some thread implementations
     329             :   // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork
     330             :   // calls dispatch on mThread) before the thread processes an event. As
     331             :   // such, clear the queue manually.
     332          56 :   while (aDelegate->DoWork()) {
     333             :   }
     334             : 
     335          56 :   base::ScopedNSAutoreleasePool autoReleasePool;
     336             :   for (;;) {
     337         215 :     autoReleasePool.Recycle();
     338             : 
     339         215 :     bool didWork = NS_ProcessNextEvent(thread, false) ? true : false;
     340         186 :     if (!keep_running_) {
     341           1 :       break;
     342             :     }
     343             : 
     344         186 :     didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
     345             : 
     346         186 :     if (didWork && delayed_work_time_.is_null()) {
     347          34 :       mDelayedWorkTimer->Cancel();
     348             :     }
     349             : 
     350         186 :     if (!keep_running_) {
     351           0 :       break;
     352             :     }
     353             : 
     354         186 :     if (didWork) {
     355          68 :       continue;
     356             :     }
     357             : 
     358         277 :     DebugOnly<bool> didIdleWork = aDelegate->DoIdleWork();
     359         152 :     MOZ_ASSERT(!didIdleWork);
     360         152 :     if (!keep_running_) {
     361           1 :       break;
     362             :     }
     363             : 
     364         151 :     if (didWork) {
     365           0 :       continue;
     366             :     }
     367             : 
     368             :     // This will either sleep or process an event.
     369         151 :     NS_ProcessNextEvent(thread, true);
     370         159 :   }
     371             : 
     372           1 :   mDelayedWorkTimer->Cancel();
     373             : 
     374           1 :   keep_running_ = true;
     375           1 : }
     376             : 
     377             : #if defined(XP_WIN)
     378             : 
     379             : NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads, nsIThreadObserver)
     380             : 
     381             : #define CHECK_QUIT_STATE { if (state_->should_quit) { break; } }
     382             : 
     383             : void
     384             : MessagePumpForNonMainUIThreads::DoRunLoop()
     385             : {
     386             :   MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
     387             : 
     388             :   // If this is a chromium thread and no nsThread is associated
     389             :   // with it, this call will create a new nsThread.
     390             :   nsIThread* thread = NS_GetCurrentThread();
     391             :   MOZ_ASSERT(thread);
     392             : 
     393             :   // Set the main thread observer so we can wake up when
     394             :   // xpcom events need to get processed.
     395             :   nsCOMPtr<nsIThreadInternal> ti(do_QueryInterface(thread));
     396             :   MOZ_ASSERT(ti);
     397             :   ti->SetObserver(this);
     398             : 
     399             :   base::ScopedNSAutoreleasePool autoReleasePool;
     400             :   for (;;) {
     401             :     autoReleasePool.Recycle();
     402             : 
     403             :     bool didWork = NS_ProcessNextEvent(thread, false);
     404             : 
     405             :     didWork |= ProcessNextWindowsMessage();
     406             :     CHECK_QUIT_STATE
     407             : 
     408             :     didWork |= state_->delegate->DoWork();
     409             :     CHECK_QUIT_STATE
     410             : 
     411             :     didWork |= state_->delegate->DoDelayedWork(&delayed_work_time_);
     412             :     if (didWork && delayed_work_time_.is_null()) {
     413             :       KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
     414             :     }
     415             :     CHECK_QUIT_STATE
     416             : 
     417             :     if (didWork) {
     418             :       continue;
     419             :     }
     420             : 
     421             :     DebugOnly<bool> didIdleWork = state_->delegate->DoIdleWork();
     422             :     MOZ_ASSERT(!didIdleWork);
     423             :     CHECK_QUIT_STATE
     424             : 
     425             :     SetInWait();
     426             :     bool hasWork = NS_HasPendingEvents(thread);
     427             :     if (didWork || hasWork) {
     428             :       ClearInWait();
     429             :       continue;
     430             :     }
     431             :     WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)
     432             :     ClearInWait();
     433             :   }
     434             : 
     435             :   ClearInWait();
     436             : 
     437             :   ti->SetObserver(nullptr);
     438             : }
     439             : 
     440             : NS_IMETHODIMP
     441             : MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal *thread)
     442             : {
     443             :   // If our thread is sleeping in DoRunLoop's call to WaitForWork() and an
     444             :   // event posts to the nsIThread event queue - break our thread out of
     445             :   // chromium's WaitForWork.
     446             :   if (GetInWait()) {
     447             :     ScheduleWork();
     448             :   }
     449             :   return NS_OK;
     450             : }
     451             : 
     452             : NS_IMETHODIMP
     453             : MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread,
     454             :                                                    bool mayWait)
     455             : {
     456             :   return NS_OK;
     457             : }
     458             : 
     459             : NS_IMETHODIMP
     460             : MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread,
     461             :                                                       bool eventWasProcessed)
     462             : {
     463             :   return NS_OK;
     464             : }
     465             : 
     466             : #endif // XP_WIN
     467             : 
     468             : #if defined(MOZ_WIDGET_ANDROID)
     469             : void
     470             : MessagePumpForAndroidUI::Run(Delegate* delegate)
     471             : {
     472             :   MOZ_CRASH("MessagePumpForAndroidUI should never be Run.");
     473             : }
     474             : 
     475             : void
     476             : MessagePumpForAndroidUI::Quit()
     477             : {
     478             :   MOZ_CRASH("MessagePumpForAndroidUI should never be Quit.");
     479             : }
     480             : 
     481             : void
     482             : MessagePumpForAndroidUI::ScheduleWork()
     483             : {
     484             :   MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleWork");
     485             : }
     486             : 
     487             : void
     488             : MessagePumpForAndroidUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time)
     489             : {
     490             :   MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleDelayedWork");
     491             : }
     492             : #endif // defined(MOZ_WIDGET_ANDROID)

Generated by: LCOV version 1.13