LCOV - code coverage report
Current view: top level - widget - nsBaseAppShell.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 79 137 57.7 %
Date: 2017-07-14 16:53:18 Functions: 14 23 60.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "base/message_loop.h"
       7             : 
       8             : #include "nsBaseAppShell.h"
       9             : #if defined(MOZ_CRASHREPORTER)
      10             : #include "nsExceptionHandler.h"
      11             : #endif
      12             : #include "nsThreadUtils.h"
      13             : #include "nsIObserverService.h"
      14             : #include "nsServiceManagerUtils.h"
      15             : #include "mozilla/Services.h"
      16             : 
      17             : // When processing the next thread event, the appshell may process native
      18             : // events (if not in performance mode), which can result in suppressing the
      19             : // next thread event for at most this many ticks:
      20             : #define THREAD_EVENT_STARVATION_LIMIT PR_MillisecondsToInterval(10)
      21             : 
      22        4697 : NS_IMPL_ISUPPORTS(nsBaseAppShell, nsIAppShell, nsIThreadObserver, nsIObserver)
      23             : 
      24           3 : nsBaseAppShell::nsBaseAppShell()
      25             :   : mSuspendNativeCount(0)
      26             :   , mEventloopNestingLevel(0)
      27             :   , mBlockedWait(nullptr)
      28             :   , mFavorPerf(0)
      29             :   , mNativeEventPending(false)
      30             :   , mStarvationDelay(0)
      31             :   , mSwitchTime(0)
      32             :   , mLastNativeEventTime(0)
      33             :   , mEventloopNestingState(eEventloopNone)
      34             :   , mRunning(false)
      35             :   , mExiting(false)
      36           3 :   , mBlockNativeEvent(false)
      37             : {
      38           3 : }
      39             : 
      40           0 : nsBaseAppShell::~nsBaseAppShell()
      41             : {
      42           0 : }
      43             : 
      44             : nsresult
      45           3 : nsBaseAppShell::Init()
      46             : {
      47             :   // Configure ourselves as an observer for the current thread:
      48             : 
      49             :   nsCOMPtr<nsIThreadInternal> threadInt =
      50           6 :       do_QueryInterface(NS_GetCurrentThread());
      51           3 :   NS_ENSURE_STATE(threadInt);
      52             : 
      53           3 :   threadInt->SetObserver(this);
      54             : 
      55             :   nsCOMPtr<nsIObserverService> obsSvc =
      56           6 :     mozilla::services::GetObserverService();
      57           3 :   if (obsSvc)
      58           3 :     obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
      59           3 :   return NS_OK;
      60             : }
      61             : 
      62             : // Called by nsAppShell's native event callback
      63             : void
      64         286 : nsBaseAppShell::NativeEventCallback()
      65             : {
      66         286 :   if (!mNativeEventPending.exchange(false))
      67           0 :     return;
      68             : 
      69             :   // If DoProcessNextNativeEvent is on the stack, then we assume that we can
      70             :   // just unwind and let nsThread::ProcessNextEvent process the next event.
      71             :   // However, if we are called from a nested native event loop (maybe via some
      72             :   // plug-in or library function), then go ahead and process Gecko events now.
      73         286 :   if (mEventloopNestingState == eEventloopXPCOM) {
      74         286 :     mEventloopNestingState = eEventloopOther;
      75             :     // XXX there is a tiny risk we will never get a new NativeEventCallback,
      76             :     // XXX see discussion in bug 389931.
      77         286 :     return;
      78             :   }
      79             : 
      80             :   // nsBaseAppShell::Run is not being used to pump events, so this may be
      81             :   // our only opportunity to process pending gecko events.
      82             : 
      83           0 :   nsIThread *thread = NS_GetCurrentThread();
      84           0 :   bool prevBlockNativeEvent = mBlockNativeEvent;
      85           0 :   if (mEventloopNestingState == eEventloopOther) {
      86           0 :     if (!NS_HasPendingEvents(thread))
      87           0 :       return;
      88             :     // We're in a nested native event loop and have some gecko events to
      89             :     // process.  While doing that we block processing native events from the
      90             :     // appshell - instead, we want to get back to the nested native event
      91             :     // loop ASAP (bug 420148).
      92           0 :     mBlockNativeEvent = true;
      93             :   }
      94             : 
      95           0 :   IncrementEventloopNestingLevel();
      96           0 :   EventloopNestingState prevVal = mEventloopNestingState;
      97           0 :   NS_ProcessPendingEvents(thread, THREAD_EVENT_STARVATION_LIMIT);
      98           0 :   mProcessedGeckoEvents = true;
      99           0 :   mEventloopNestingState = prevVal;
     100           0 :   mBlockNativeEvent = prevBlockNativeEvent;
     101             : 
     102             :   // Continue processing pending events later (we don't want to starve the
     103             :   // embedders event loop).
     104           0 :   if (NS_HasPendingEvents(thread))
     105           0 :     DoProcessMoreGeckoEvents();
     106             : 
     107           0 :   DecrementEventloopNestingLevel();
     108             : }
     109             : 
     110             : // Note, this is currently overidden on windows, see comments in nsAppShell for
     111             : // details.
     112             : void
     113           0 : nsBaseAppShell::DoProcessMoreGeckoEvents()
     114             : {
     115           0 :   OnDispatchedEvent(nullptr);
     116           0 : }
     117             : 
     118             : 
     119             : // Main thread via OnProcessNextEvent below
     120             : bool
     121        1314 : nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait)
     122             : {
     123             :   // The next native event to be processed may trigger our NativeEventCallback,
     124             :   // in which case we do not want it to process any thread events since we'll
     125             :   // do that when this function returns.
     126             :   //
     127             :   // If the next native event is not our NativeEventCallback, then we may end
     128             :   // up recursing into this function.
     129             :   //
     130             :   // However, if the next native event is not our NativeEventCallback, but it
     131             :   // results in another native event loop, then our NativeEventCallback could
     132             :   // fire and it will see mEventloopNestingState as eEventloopOther.
     133             :   //
     134        1314 :   EventloopNestingState prevVal = mEventloopNestingState;
     135        1314 :   mEventloopNestingState = eEventloopXPCOM;
     136             : 
     137        1314 :   IncrementEventloopNestingLevel();
     138        1314 :   bool result = ProcessNextNativeEvent(mayWait);
     139        1314 :   DecrementEventloopNestingLevel();
     140             : 
     141        1314 :   mEventloopNestingState = prevVal;
     142        1314 :   return result;
     143             : }
     144             : 
     145             : //-------------------------------------------------------------------------
     146             : // nsIAppShell methods:
     147             : 
     148             : NS_IMETHODIMP
     149           3 : nsBaseAppShell::Run(void)
     150             : {
     151           3 :   NS_ENSURE_STATE(!mRunning);  // should not call Run twice
     152           3 :   mRunning = true;
     153             : 
     154           3 :   nsIThread *thread = NS_GetCurrentThread();
     155             : 
     156           3 :   MessageLoop::current()->Run();
     157             : 
     158           0 :   NS_ProcessPendingEvents(thread);
     159             : 
     160           0 :   mRunning = false;
     161           0 :   return NS_OK;
     162             : }
     163             : 
     164             : NS_IMETHODIMP
     165           0 : nsBaseAppShell::Exit(void)
     166             : {
     167           0 :   if (mRunning && !mExiting) {
     168           0 :     MessageLoop::current()->Quit();
     169             :   }
     170           0 :   mExiting = true;
     171           0 :   return NS_OK;
     172             : }
     173             : 
     174             : NS_IMETHODIMP
     175           6 : nsBaseAppShell::FavorPerformanceHint(bool favorPerfOverStarvation,
     176             :                                      uint32_t starvationDelay)
     177             : {
     178           6 :   mStarvationDelay = PR_MillisecondsToInterval(starvationDelay);
     179           6 :   if (favorPerfOverStarvation) {
     180           3 :     ++mFavorPerf;
     181             :   } else {
     182           3 :     --mFavorPerf;
     183           3 :     mSwitchTime = PR_IntervalNow();
     184             :   }
     185           6 :   return NS_OK;
     186             : }
     187             : 
     188             : NS_IMETHODIMP
     189           0 : nsBaseAppShell::SuspendNative()
     190             : {
     191           0 :   ++mSuspendNativeCount;
     192           0 :   return NS_OK;
     193             : }
     194             : 
     195             : NS_IMETHODIMP
     196           0 : nsBaseAppShell::ResumeNative()
     197             : {
     198           0 :   --mSuspendNativeCount;
     199           0 :   NS_ASSERTION(mSuspendNativeCount >= 0, "Unbalanced call to nsBaseAppShell::ResumeNative!");
     200           0 :   return NS_OK;
     201             : }
     202             : 
     203             : NS_IMETHODIMP
     204           0 : nsBaseAppShell::GetEventloopNestingLevel(uint32_t* aNestingLevelResult)
     205             : {
     206           0 :   NS_ENSURE_ARG_POINTER(aNestingLevelResult);
     207             : 
     208           0 :   *aNestingLevelResult = mEventloopNestingLevel;
     209             : 
     210           0 :   return NS_OK;
     211             : }
     212             : 
     213             : //-------------------------------------------------------------------------
     214             : // nsIThreadObserver methods:
     215             : 
     216             : // Called from any thread
     217             : NS_IMETHODIMP
     218        1093 : nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr)
     219             : {
     220        1093 :   if (mBlockNativeEvent)
     221           0 :     return NS_OK;
     222             : 
     223        1093 :   if (mNativeEventPending.exchange(true))
     224         804 :     return NS_OK;
     225             : 
     226             :   // Returns on the main thread in NativeEventCallback above
     227         289 :   ScheduleNativeEventCallback();
     228         289 :   return NS_OK;
     229             : }
     230             : 
     231             : // Called from the main thread
     232             : NS_IMETHODIMP
     233        1230 : nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait)
     234             : {
     235        1230 :   if (mBlockNativeEvent) {
     236           0 :     if (!mayWait)
     237           0 :       return NS_OK;
     238             :     // Hmm, we're in a nested native event loop and would like to get
     239             :     // back to it ASAP, but it seems a gecko event has caused us to
     240             :     // spin up a nested XPCOM event loop (eg. modal window), so we
     241             :     // really must start processing native events here again.
     242           0 :     mBlockNativeEvent = false;
     243           0 :     if (NS_HasPendingEvents(thr))
     244           0 :       OnDispatchedEvent(thr); // in case we blocked it earlier
     245             :   }
     246             : 
     247        1230 :   PRIntervalTime start = PR_IntervalNow();
     248        1230 :   PRIntervalTime limit = THREAD_EVENT_STARVATION_LIMIT;
     249             : 
     250             :   // Unblock outer nested wait loop (below).
     251        1230 :   if (mBlockedWait)
     252           0 :     *mBlockedWait = false;
     253             : 
     254        1230 :   bool *oldBlockedWait = mBlockedWait;
     255        1230 :   mBlockedWait = &mayWait;
     256             : 
     257             :   // When mayWait is true, we need to make sure that there is an event in the
     258             :   // thread's event queue before we return.  Otherwise, the thread will block
     259             :   // on its event queue waiting for an event.
     260        1230 :   bool needEvent = mayWait;
     261             :   // Reset prior to invoking DoProcessNextNativeEvent which might cause
     262             :   // NativeEventCallback to process gecko events.
     263        1230 :   mProcessedGeckoEvents = false;
     264             : 
     265        1230 :   if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) {
     266             :     // Favor pending native events
     267         850 :     PRIntervalTime now = start;
     268             :     bool keepGoing;
     269        1143 :     do {
     270        1143 :       mLastNativeEventTime = now;
     271        1143 :       keepGoing = DoProcessNextNativeEvent(false);
     272        1993 :     } while (keepGoing && ((now = PR_IntervalNow()) - start) < limit);
     273             :   } else {
     274             :     // Avoid starving native events completely when in performance mode
     275         380 :     if (start - mLastNativeEventTime > limit) {
     276          24 :       mLastNativeEventTime = start;
     277          24 :       DoProcessNextNativeEvent(false);
     278             :     }
     279             :   }
     280             : 
     281        1374 :   while (!NS_HasPendingEvents(thr) && !mProcessedGeckoEvents) {
     282             :     // If we have been asked to exit from Run, then we should not wait for
     283             :     // events to process.  Note that an inner nested event loop causes
     284             :     // 'mayWait' to become false too, through 'mBlockedWait'.
     285         147 :     if (mExiting)
     286           0 :       mayWait = false;
     287             : 
     288         147 :     mLastNativeEventTime = PR_IntervalNow();
     289         147 :     if (!DoProcessNextNativeEvent(mayWait) || !mayWait)
     290          75 :       break;
     291             :   }
     292             : 
     293        1230 :   mBlockedWait = oldBlockedWait;
     294             : 
     295             :   // Make sure that the thread event queue does not block on its monitor, as
     296             :   // it normally would do if it did not have any pending events.  To avoid
     297             :   // that, we simply insert a dummy event into its queue during shutdown.
     298        1230 :   if (needEvent && !mExiting && !NS_HasPendingEvents(thr)) {
     299           0 :     DispatchDummyEvent(thr);
     300             :   }
     301             : 
     302        1230 :   return NS_OK;
     303             : }
     304             : 
     305             : bool
     306           0 : nsBaseAppShell::DispatchDummyEvent(nsIThread* aTarget)
     307             : {
     308           0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     309             : 
     310           0 :   if (!mDummyEvent)
     311           0 :     mDummyEvent = new mozilla::Runnable("DummyEvent");
     312             : 
     313           0 :   return NS_SUCCEEDED(aTarget->Dispatch(mDummyEvent, NS_DISPATCH_NORMAL));
     314             : }
     315             : 
     316             : void
     317        1314 : nsBaseAppShell::IncrementEventloopNestingLevel()
     318             : {
     319        1314 :   ++mEventloopNestingLevel;
     320             : #if defined(MOZ_CRASHREPORTER)
     321        1314 :   CrashReporter::SetEventloopNestingLevel(mEventloopNestingLevel);
     322             : #endif
     323        1314 : }
     324             : 
     325             : void
     326        1314 : nsBaseAppShell::DecrementEventloopNestingLevel()
     327             : {
     328        1314 :   --mEventloopNestingLevel;
     329             : #if defined(MOZ_CRASHREPORTER)
     330        1314 :   CrashReporter::SetEventloopNestingLevel(mEventloopNestingLevel);
     331             : #endif
     332        1314 : }
     333             : 
     334             : // Called from the main thread
     335             : NS_IMETHODIMP
     336        1227 : nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr,
     337             :                                       bool eventWasProcessed)
     338             : {
     339        1227 :   return NS_OK;
     340             : }
     341             : 
     342             : NS_IMETHODIMP
     343           0 : nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
     344             :                         const char16_t *data)
     345             : {
     346           0 :   NS_ASSERTION(!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID), "oops");
     347           0 :   Exit();
     348           0 :   return NS_OK;
     349             : }

Generated by: LCOV version 1.13