LCOV - code coverage report
Current view: top level - dom/media - MediaTimer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 86 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 13 0.0 %
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 "MediaTimer.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/RefPtr.h"
      11             : #include "mozilla/SharedThreadPool.h"
      12             : #include "nsComponentManagerUtils.h"
      13             : #include "nsThreadUtils.h"
      14             : #include <math.h>
      15             : 
      16             : namespace mozilla {
      17             : 
      18           0 : NS_IMPL_ADDREF(MediaTimer)
      19           0 : NS_IMPL_RELEASE_WITH_DESTROY(MediaTimer, DispatchDestroy())
      20             : 
      21           0 : MediaTimer::MediaTimer()
      22             :   : mMonitor("MediaTimer Monitor")
      23           0 :   , mTimer(do_CreateInstance("@mozilla.org/timer;1"))
      24             :   , mCreationTimeStamp(TimeStamp::Now())
      25           0 :   , mUpdateScheduled(false)
      26             : {
      27           0 :   TIMER_LOG("MediaTimer::MediaTimer");
      28             : 
      29             :   // Use the SharedThreadPool to create an nsIThreadPool with a maximum of one
      30             :   // thread, which is equivalent to an nsIThread for our purposes.
      31             :   RefPtr<SharedThreadPool> threadPool(
      32           0 :     SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaTimer"), 1));
      33           0 :   mThread = threadPool.get();
      34           0 :   mTimer->SetTarget(mThread);
      35           0 : }
      36             : 
      37             : void
      38           0 : MediaTimer::DispatchDestroy()
      39             : {
      40             :   // Hold a strong reference to the thread so that it doesn't get deleted in
      41             :   // Destroy(), which may run completely before the stack if Dispatch() begins
      42             :   // to unwind.
      43           0 :   nsCOMPtr<nsIEventTarget> thread = mThread;
      44             :   nsresult rv =
      45           0 :     thread->Dispatch(NewNonOwningRunnableMethod(
      46             :                        "MediaTimer::Destroy", this, &MediaTimer::Destroy),
      47           0 :                      NS_DISPATCH_NORMAL);
      48           0 :   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
      49             :   (void) rv;
      50           0 : }
      51             : 
      52             : void
      53           0 : MediaTimer::Destroy()
      54             : {
      55           0 :   MOZ_ASSERT(OnMediaTimerThread());
      56           0 :   TIMER_LOG("MediaTimer::Destroy");
      57             : 
      58             :   // Reject any outstanding entries. There's no need to acquire the monitor
      59             :   // here, because we're on the timer thread and all other references to us
      60             :   // must be gone.
      61           0 :   while (!mEntries.empty()) {
      62           0 :     mEntries.top().mPromise->Reject(false, __func__);
      63           0 :     mEntries.pop();
      64             :   }
      65             : 
      66             :   // Cancel the timer if necessary.
      67           0 :   CancelTimerIfArmed();
      68             : 
      69           0 :   delete this;
      70           0 : }
      71             : 
      72             : bool
      73           0 : MediaTimer::OnMediaTimerThread()
      74             : {
      75           0 :   bool rv = false;
      76           0 :   mThread->IsOnCurrentThread(&rv);
      77           0 :   return rv;
      78             : }
      79             : 
      80             : RefPtr<MediaTimerPromise>
      81           0 : MediaTimer::WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite)
      82             : {
      83           0 :   MonitorAutoLock mon(mMonitor);
      84           0 :   TIMER_LOG("MediaTimer::WaitUntil %" PRId64, RelativeMicroseconds(aTimeStamp));
      85           0 :   Entry e(aTimeStamp, aCallSite);
      86           0 :   RefPtr<MediaTimerPromise> p = e.mPromise.get();
      87           0 :   mEntries.push(e);
      88           0 :   ScheduleUpdate();
      89           0 :   return p;
      90             : }
      91             : 
      92             : void
      93           0 : MediaTimer::ScheduleUpdate()
      94             : {
      95           0 :   mMonitor.AssertCurrentThreadOwns();
      96           0 :   if (mUpdateScheduled) {
      97           0 :     return;
      98             :   }
      99           0 :   mUpdateScheduled = true;
     100             : 
     101           0 :   nsresult rv = mThread->Dispatch(
     102           0 :     NewRunnableMethod("MediaTimer::Update", this, &MediaTimer::Update),
     103           0 :     NS_DISPATCH_NORMAL);
     104           0 :   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     105             :   (void) rv;
     106             : }
     107             : 
     108             : void
     109           0 : MediaTimer::Update()
     110             : {
     111           0 :   MonitorAutoLock mon(mMonitor);
     112           0 :   UpdateLocked();
     113           0 : }
     114             : 
     115             : void
     116           0 : MediaTimer::UpdateLocked()
     117             : {
     118           0 :   MOZ_ASSERT(OnMediaTimerThread());
     119           0 :   mMonitor.AssertCurrentThreadOwns();
     120           0 :   mUpdateScheduled = false;
     121             : 
     122           0 :   TIMER_LOG("MediaTimer::UpdateLocked");
     123             : 
     124             :   // Resolve all the promises whose time is up.
     125           0 :   TimeStamp now = TimeStamp::Now();
     126           0 :   while (!mEntries.empty() && mEntries.top().mTimeStamp <= now) {
     127           0 :     mEntries.top().mPromise->Resolve(true, __func__);
     128           0 :     DebugOnly<TimeStamp> poppedTimeStamp = mEntries.top().mTimeStamp;
     129           0 :     mEntries.pop();
     130           0 :     MOZ_ASSERT_IF(!mEntries.empty(), *&poppedTimeStamp <= mEntries.top().mTimeStamp);
     131             :   }
     132             : 
     133             :   // If we've got no more entries, cancel any pending timer and bail out.
     134           0 :   if (mEntries.empty()) {
     135           0 :     CancelTimerIfArmed();
     136           0 :     return;
     137             :   }
     138             : 
     139             :   // We've got more entries - (re)arm the timer for the soonest one.
     140           0 :   if (!TimerIsArmed() || mEntries.top().mTimeStamp < mCurrentTimerTarget) {
     141           0 :     CancelTimerIfArmed();
     142           0 :     ArmTimer(mEntries.top().mTimeStamp, now);
     143             :   }
     144             : }
     145             : 
     146             : /*
     147             :  * We use a callback function, rather than a callback method, to ensure that
     148             :  * the nsITimer does not artifically keep the refcount of the MediaTimer above
     149             :  * zero. When the MediaTimer is destroyed, it safely cancels the nsITimer so that
     150             :  * we never fire against a dangling closure.
     151             :  */
     152             : 
     153             : /* static */ void
     154           0 : MediaTimer::TimerCallback(nsITimer* aTimer, void* aClosure)
     155             : {
     156           0 :   static_cast<MediaTimer*>(aClosure)->TimerFired();
     157           0 : }
     158             : 
     159             : void
     160           0 : MediaTimer::TimerFired()
     161             : {
     162           0 :   MonitorAutoLock mon(mMonitor);
     163           0 :   MOZ_ASSERT(OnMediaTimerThread());
     164           0 :   mCurrentTimerTarget = TimeStamp();
     165           0 :   UpdateLocked();
     166           0 : }
     167             : 
     168             : void
     169           0 : MediaTimer::ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow)
     170             : {
     171           0 :   MOZ_DIAGNOSTIC_ASSERT(!TimerIsArmed());
     172           0 :   MOZ_DIAGNOSTIC_ASSERT(aTarget > aNow);
     173             : 
     174             :   // XPCOM timer resolution is in milliseconds. It's important to never resolve
     175             :   // a timer when mTarget might compare < now (even if very close), so round up.
     176           0 :   unsigned long delay = std::ceil((aTarget - aNow).ToMilliseconds());
     177           0 :   TIMER_LOG("MediaTimer::ArmTimer delay=%lu", delay);
     178           0 :   mCurrentTimerTarget = aTarget;
     179           0 :   nsresult rv = mTimer->InitWithNamedFuncCallback(&TimerCallback, this, delay,
     180             :                                                   nsITimer::TYPE_ONE_SHOT,
     181           0 :                                                   "MediaTimer::TimerCallback");
     182           0 :   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     183             :   (void) rv;
     184           0 : }
     185             : 
     186             : } // namespace mozilla

Generated by: LCOV version 1.13