LCOV - code coverage report
Current view: top level - dom/base - TimeoutExecutor.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 83 101 82.2 %
Date: 2017-07-14 16:53:18 Functions: 14 17 82.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       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 "TimeoutExecutor.h"
       8             : 
       9             : namespace mozilla {
      10             : namespace dom {
      11             : 
      12         116 : NS_IMPL_ISUPPORTS(TimeoutExecutor, nsIRunnable, nsITimerCallback, nsINamed)
      13             : 
      14           0 : TimeoutExecutor::~TimeoutExecutor()
      15             : {
      16             :   // The TimeoutManager should keep the Executor alive until its destroyed,
      17             :   // and then call Shutdown() explicitly.
      18           0 :   MOZ_DIAGNOSTIC_ASSERT(mMode == Mode::Shutdown);
      19           0 :   MOZ_DIAGNOSTIC_ASSERT(!mOwner);
      20           0 :   MOZ_DIAGNOSTIC_ASSERT(!mTimer);
      21           0 : }
      22             : 
      23             : nsresult
      24           6 : TimeoutExecutor::ScheduleImmediate(const TimeStamp& aDeadline,
      25             :                                    const TimeStamp& aNow)
      26             : {
      27           6 :   MOZ_DIAGNOSTIC_ASSERT(mDeadline.IsNull());
      28           6 :   MOZ_DIAGNOSTIC_ASSERT(mMode == Mode::None);
      29           6 :   MOZ_DIAGNOSTIC_ASSERT(aDeadline <= (aNow + mAllowedEarlyFiringTime));
      30             : 
      31             :   nsresult rv =
      32           6 :     mOwner->EventTarget()->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
      33           6 :   NS_ENSURE_SUCCESS(rv, rv);
      34             : 
      35           6 :   mMode = Mode::Immediate;
      36           6 :   mDeadline = aDeadline;
      37             : 
      38           6 :   return NS_OK;
      39             : }
      40             : 
      41             : nsresult
      42          10 : TimeoutExecutor::ScheduleDelayed(const TimeStamp& aDeadline,
      43             :                                  const TimeStamp& aNow,
      44             :                                  const TimeDuration& aMinDelay)
      45             : {
      46          10 :   MOZ_DIAGNOSTIC_ASSERT(mDeadline.IsNull());
      47          10 :   MOZ_DIAGNOSTIC_ASSERT(mMode == Mode::None);
      48          10 :   MOZ_DIAGNOSTIC_ASSERT(!aMinDelay.IsZero() ||
      49             :                         aDeadline > (aNow + mAllowedEarlyFiringTime));
      50             : 
      51          10 :   nsresult rv = NS_OK;
      52             : 
      53          10 :   if (!mTimer) {
      54           2 :     mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
      55           2 :     NS_ENSURE_SUCCESS(rv, rv);
      56             : 
      57           2 :     uint32_t earlyMicros = 0;
      58           2 :     MOZ_ALWAYS_SUCCEEDS(mTimer->GetAllowedEarlyFiringMicroseconds(&earlyMicros));
      59           2 :     mAllowedEarlyFiringTime = TimeDuration::FromMicroseconds(earlyMicros);
      60             :   }
      61             : 
      62             :   // Always call Cancel() in case we are re-using a timer.  Otherwise
      63             :   // the subsequent SetTarget() may fail.
      64          10 :   rv = mTimer->Cancel();
      65          10 :   NS_ENSURE_SUCCESS(rv, rv);
      66             : 
      67          10 :   rv = mTimer->SetTarget(mOwner->EventTarget());
      68          10 :   NS_ENSURE_SUCCESS(rv, rv);
      69             : 
      70             :   // Calculate the delay based on the deadline and current time.  If we have
      71             :   // a minimum delay set then clamp to that value.
      72             :   //
      73             :   // Note, we don't actually adjust our mDeadline for the minimum delay, just
      74             :   // the nsITimer value.  This is necessary to avoid lots of needless
      75             :   // rescheduling if more deadlines come in between now and the minimum delay
      76             :   // firing time.
      77          10 :   TimeDuration delay = TimeDuration::Max(aMinDelay, aDeadline - aNow);
      78             : 
      79             :   // Note, we cannot use the normal nsITimer init methods that take
      80             :   // integer milliseconds.  We need higher precision.  Consider this
      81             :   // situation:
      82             :   //
      83             :   // 1. setTimeout(f, 1);
      84             :   // 2. do some work for 500us
      85             :   // 3. setTimeout(g, 1);
      86             :   //
      87             :   // This should fire f() and g() 500us apart.
      88             :   //
      89             :   // In the past worked because each setTimeout() got its own nsITimer.  The 1ms
      90             :   // was preserved and passed through to nsITimer which converted it to a
      91             :   // TimeStamp, etc.
      92             :   //
      93             :   // Now, however, there is only one nsITimer.  We fire f() and then try to
      94             :   // schedule a new nsITimer for g().  Its only 500us in the future, though.  We
      95             :   // must be able to pass this fractional value to nsITimer in order to get an
      96             :   // accurate wakeup time.
      97          20 :   rv = mTimer->InitHighResolutionWithCallback(this, delay,
      98          10 :                                               nsITimer::TYPE_ONE_SHOT);
      99          10 :   NS_ENSURE_SUCCESS(rv, rv);
     100             : 
     101          10 :   mMode = Mode::Delayed;
     102          10 :   mDeadline = aDeadline;
     103             : 
     104          10 :   return NS_OK;
     105             : }
     106             : 
     107             : nsresult
     108          16 : TimeoutExecutor::Schedule(const TimeStamp& aDeadline,
     109             :                           const TimeDuration& aMinDelay)
     110             : {
     111          16 :   TimeStamp now(TimeStamp::Now());
     112             : 
     113             :   // Schedule an immediate runnable if the desired deadline has passed
     114             :   // or is slightly in the future.  This is similar to how nsITimer will
     115             :   // fire timers early based on the interval resolution.
     116          16 :   if (aMinDelay.IsZero() && aDeadline <= (now + mAllowedEarlyFiringTime)) {
     117           6 :     return ScheduleImmediate(aDeadline, now);
     118             :   }
     119             : 
     120          10 :   return ScheduleDelayed(aDeadline, now, aMinDelay);
     121             : }
     122             : 
     123             : nsresult
     124          16 : TimeoutExecutor::MaybeReschedule(const TimeStamp& aDeadline,
     125             :                                  const TimeDuration& aMinDelay)
     126             : {
     127          16 :   MOZ_DIAGNOSTIC_ASSERT(!mDeadline.IsNull());
     128          16 :   MOZ_DIAGNOSTIC_ASSERT(mMode == Mode::Immediate ||
     129             :                         mMode == Mode::Delayed);
     130             : 
     131          16 :   if (aDeadline >= mDeadline) {
     132          10 :     return NS_OK;
     133             :   }
     134             : 
     135           6 :   if (mMode == Mode::Immediate) {
     136             :     // Don't reduce the deadline here as we want to execute the
     137             :     // timer we originally scheduled even if its a few microseconds
     138             :     // in the future.
     139           0 :     return NS_OK;
     140             :   }
     141             : 
     142           6 :   Cancel();
     143           6 :   return Schedule(aDeadline, aMinDelay);
     144             : }
     145             : 
     146             : void
     147           8 : TimeoutExecutor::MaybeExecute()
     148             : {
     149           8 :   MOZ_DIAGNOSTIC_ASSERT(mMode != Mode::Shutdown && mMode != Mode::None);
     150           8 :   MOZ_DIAGNOSTIC_ASSERT(mOwner);
     151           8 :   MOZ_DIAGNOSTIC_ASSERT(!mDeadline.IsNull());
     152             : 
     153           8 :   TimeStamp deadline(mDeadline);
     154             : 
     155             :   // Sometimes nsITimer or canceled timers will fire too early.  If this
     156             :   // happens then just cap our deadline to our maximum time in the future
     157             :   // and proceed.  If there are no timers ready we will get rescheduled
     158             :   // by TimeoutManager.
     159           8 :   TimeStamp now(TimeStamp::Now());
     160           8 :   TimeStamp limit = now + mAllowedEarlyFiringTime;
     161           8 :   if (deadline > limit) {
     162           0 :     deadline = limit;
     163             :   }
     164             : 
     165           8 :   Cancel();
     166             : 
     167           8 :   mOwner->RunTimeout(now, deadline);
     168           8 : }
     169             : 
     170           7 : TimeoutExecutor::TimeoutExecutor(TimeoutManager* aOwner)
     171             :   : mOwner(aOwner)
     172           7 :   , mMode(Mode::None)
     173             : {
     174           7 :   MOZ_DIAGNOSTIC_ASSERT(mOwner);
     175           7 : }
     176             : 
     177             : void
     178           0 : TimeoutExecutor::Shutdown()
     179             : {
     180           0 :   mOwner = nullptr;
     181             : 
     182           0 :   if (mTimer) {
     183           0 :     mTimer->Cancel();
     184           0 :     mTimer = nullptr;
     185             :   }
     186             : 
     187           0 :   mMode = Mode::Shutdown;
     188           0 :   mDeadline = TimeStamp();
     189           0 : }
     190             : 
     191             : nsresult
     192          26 : TimeoutExecutor::MaybeSchedule(const TimeStamp& aDeadline,
     193             :                                const TimeDuration& aMinDelay)
     194             : {
     195          26 :   MOZ_DIAGNOSTIC_ASSERT(!aDeadline.IsNull());
     196             : 
     197          26 :   if (mMode == Mode::Shutdown) {
     198           0 :     return NS_OK;
     199             :   }
     200             : 
     201          26 :   if (mMode == Mode::Immediate || mMode == Mode::Delayed) {
     202          16 :     return MaybeReschedule(aDeadline, aMinDelay);
     203             :   }
     204             : 
     205          10 :   return Schedule(aDeadline, aMinDelay);
     206             : }
     207             : 
     208             : void
     209          17 : TimeoutExecutor::Cancel()
     210             : {
     211          17 :   if (mTimer) {
     212          14 :     mTimer->Cancel();
     213             :   }
     214          17 :   mMode = Mode::None;
     215          17 :   mDeadline = TimeStamp();
     216          17 : }
     217             : 
     218             : NS_IMETHODIMP
     219           5 : TimeoutExecutor::Run()
     220             : {
     221             :   // If the executor is canceled and then rescheduled its possible to get
     222             :   // spurious executions here.  Ignore these unless our current mode matches.
     223           5 :   if (mMode == Mode::Immediate) {
     224           5 :     MaybeExecute();
     225             :   }
     226           5 :   return NS_OK;
     227             : }
     228             : 
     229             : NS_IMETHODIMP
     230           3 : TimeoutExecutor::Notify(nsITimer* aTimer)
     231             : {
     232             :   // If the executor is canceled and then rescheduled its possible to get
     233             :   // spurious executions here.  Ignore these unless our current mode matches.
     234           3 :   if (mMode == Mode::Delayed) {
     235           3 :     MaybeExecute();
     236             :   }
     237           3 :   return NS_OK;
     238             : }
     239             : 
     240             : NS_IMETHODIMP
     241           9 : TimeoutExecutor::GetName(nsACString& aNameOut)
     242             : {
     243           9 :   aNameOut.AssignLiteral("TimeoutExecutor Runnable");
     244           9 :   return NS_OK;
     245             : }
     246             : 
     247             : NS_IMETHODIMP
     248           0 : TimeoutExecutor::SetName(const char* aName)
     249             : {
     250           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     251             : }
     252             : 
     253             : } // namespace dom
     254             : } // namespace mozilla

Generated by: LCOV version 1.13