LCOV - code coverage report
Current view: top level - xpcom/threads - TimerThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 230 324 71.0 %
Date: 2017-07-14 16:53:18 Functions: 35 44 79.5 %
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 "nsTimerImpl.h"
       8             : #include "TimerThread.h"
       9             : 
      10             : #include "nsThreadUtils.h"
      11             : #include "pratom.h"
      12             : 
      13             : #include "nsIObserverService.h"
      14             : #include "nsIServiceManager.h"
      15             : #include "mozilla/Services.h"
      16             : #include "mozilla/ChaosMode.h"
      17             : #include "mozilla/ArenaAllocator.h"
      18             : #include "mozilla/ArrayUtils.h"
      19             : #include "mozilla/BinarySearch.h"
      20             : 
      21             : #include <math.h>
      22             : 
      23             : using namespace mozilla;
      24             : #ifdef MOZ_TASK_TRACER
      25             : #include "GeckoTaskTracerImpl.h"
      26             : using namespace mozilla::tasktracer;
      27             : #endif
      28             : 
      29          54 : NS_IMPL_ISUPPORTS(TimerThread, nsIRunnable, nsIObserver)
      30             : 
      31           3 : TimerThread::TimerThread() :
      32             :   mInitialized(false),
      33             :   mMonitor("TimerThread.mMonitor"),
      34             :   mShutdown(false),
      35             :   mWaiting(false),
      36             :   mNotified(false),
      37             :   mSleeping(false),
      38           3 :   mAllowedEarlyFiringMicroseconds(0)
      39             : {
      40           3 : }
      41             : 
      42           0 : TimerThread::~TimerThread()
      43             : {
      44           0 :   mThread = nullptr;
      45             : 
      46           0 :   NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
      47           0 : }
      48             : 
      49             : nsresult
      50           3 : TimerThread::InitLocks()
      51             : {
      52           3 :   return NS_OK;
      53             : }
      54             : 
      55             : namespace {
      56             : 
      57           9 : class TimerObserverRunnable : public Runnable
      58             : {
      59             : public:
      60           3 :   explicit TimerObserverRunnable(nsIObserver* aObserver)
      61           3 :     : mozilla::Runnable("TimerObserverRunnable")
      62           3 :     , mObserver(aObserver)
      63             :   {
      64           3 :   }
      65             : 
      66             :   NS_DECL_NSIRUNNABLE
      67             : 
      68             : private:
      69             :   nsCOMPtr<nsIObserver> mObserver;
      70             : };
      71             : 
      72             : NS_IMETHODIMP
      73           3 : TimerObserverRunnable::Run()
      74             : {
      75             :   nsCOMPtr<nsIObserverService> observerService =
      76           6 :     mozilla::services::GetObserverService();
      77           3 :   if (observerService) {
      78           3 :     observerService->AddObserver(mObserver, "sleep_notification", false);
      79           3 :     observerService->AddObserver(mObserver, "wake_notification", false);
      80           3 :     observerService->AddObserver(mObserver, "suspend_process_notification", false);
      81           3 :     observerService->AddObserver(mObserver, "resume_process_notification", false);
      82             :   }
      83           6 :   return NS_OK;
      84             : }
      85             : 
      86             : } // namespace
      87             : 
      88             : namespace {
      89             : 
      90             : // TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
      91             : // It's needed to avoid contention over the default allocator lock when
      92             : // firing timer events (see bug 733277).  The thread-safety is required because
      93             : // nsTimerEvent objects are allocated on the timer thread, and freed on another
      94             : // thread.  Because TimerEventAllocator has its own lock, contention over that
      95             : // lock is limited to the allocation and deallocation of nsTimerEvent objects.
      96             : //
      97             : // Because this is layered over ArenaAllocator, it never shrinks -- even
      98             : // "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
      99             : // for later recycling.  So the amount of memory consumed will always be equal
     100             : // to the high-water mark consumption.  But nsTimerEvents are small and it's
     101             : // unusual to have more than a few hundred of them, so this shouldn't be a
     102             : // problem in practice.
     103             : 
     104             : class TimerEventAllocator
     105             : {
     106             : private:
     107             :   struct FreeEntry
     108             :   {
     109             :     FreeEntry* mNext;
     110             :   };
     111             : 
     112             :   ArenaAllocator<4096> mPool;
     113             :   FreeEntry* mFirstFree;
     114             :   mozilla::Monitor mMonitor;
     115             : 
     116             : public:
     117           3 :   TimerEventAllocator()
     118           3 :     : mPool()
     119             :     , mFirstFree(nullptr)
     120           3 :     , mMonitor("TimerEventAllocator")
     121             :   {
     122           3 :   }
     123             : 
     124           0 :   ~TimerEventAllocator()
     125           0 :   {
     126           0 :   }
     127             : 
     128             :   void* Alloc(size_t aSize);
     129             :   void Free(void* aPtr);
     130             : };
     131             : 
     132             : } // namespace
     133             : 
     134             : // This is a nsICancelableRunnable because we can dispatch it to Workers and
     135             : // those can be shut down at any time, and in these cases, Cancel() is called
     136             : // instead of Run().
     137             : class nsTimerEvent final : public CancelableRunnable
     138             : {
     139             : public:
     140             :   NS_IMETHOD Run() override;
     141             : 
     142           0 :   nsresult Cancel() override
     143             :   {
     144           0 :     mTimer->Cancel();
     145           0 :     return NS_OK;
     146             :   }
     147             : 
     148             :   NS_IMETHOD GetName(nsACString& aName) override;
     149             : 
     150          90 :   nsTimerEvent()
     151          90 :     : mozilla::CancelableRunnable("nsTimerEvent")
     152             :     , mTimer()
     153          90 :     , mGeneration(0)
     154             :   {
     155             :     // Note: We override operator new for this class, and the override is
     156             :     // fallible!
     157          90 :     sAllocatorUsers++;
     158          90 :   }
     159             : 
     160             :   TimeStamp mInitTime;
     161             : 
     162             :   static void Init();
     163             :   static void Shutdown();
     164             :   static void DeleteAllocatorIfNeeded();
     165             : 
     166          90 :   static void* operator new(size_t aSize) CPP_THROW_NEW
     167             :   {
     168          90 :     return sAllocator->Alloc(aSize);
     169             :   }
     170          78 :   void operator delete(void* aPtr)
     171             :   {
     172          78 :     sAllocator->Free(aPtr);
     173          78 :     DeleteAllocatorIfNeeded();
     174          78 :   }
     175             : 
     176           0 :   already_AddRefed<nsTimerImpl> ForgetTimer()
     177             :   {
     178           0 :     return mTimer.forget();
     179             :   }
     180             : 
     181          90 :   void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
     182             :   {
     183          90 :     mTimer = aTimer;
     184          90 :     mGeneration = mTimer->GetGeneration();
     185          90 :   }
     186             : 
     187             : private:
     188             :   nsTimerEvent(const nsTimerEvent&) = delete;
     189             :   nsTimerEvent& operator=(const nsTimerEvent&) = delete;
     190             :   nsTimerEvent& operator=(const nsTimerEvent&&) = delete;
     191             : 
     192         156 :   ~nsTimerEvent()
     193         156 :   {
     194          78 :     MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
     195             :                "This will result in us attempting to deallocate the nsTimerEvent allocator twice");
     196          78 :     sAllocatorUsers--;
     197         156 :   }
     198             : 
     199             :   RefPtr<nsTimerImpl> mTimer;
     200             :   int32_t      mGeneration;
     201             : 
     202             :   static TimerEventAllocator* sAllocator;
     203             :   static Atomic<int32_t> sAllocatorUsers;
     204             :   static bool sCanDeleteAllocator;
     205             : };
     206             : 
     207             : TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
     208             : Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
     209             : bool nsTimerEvent::sCanDeleteAllocator = false;
     210             : 
     211             : namespace {
     212             : 
     213             : void*
     214          90 : TimerEventAllocator::Alloc(size_t aSize)
     215             : {
     216          90 :   MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
     217             : 
     218         180 :   mozilla::MonitorAutoLock lock(mMonitor);
     219             : 
     220             :   void* p;
     221          90 :   if (mFirstFree) {
     222          69 :     p = mFirstFree;
     223          69 :     mFirstFree = mFirstFree->mNext;
     224             :   } else {
     225          21 :     p = mPool.Allocate(aSize, fallible);
     226             :   }
     227             : 
     228         180 :   return p;
     229             : }
     230             : 
     231             : void
     232          78 : TimerEventAllocator::Free(void* aPtr)
     233             : {
     234         156 :   mozilla::MonitorAutoLock lock(mMonitor);
     235             : 
     236          78 :   FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
     237             : 
     238          78 :   entry->mNext = mFirstFree;
     239          78 :   mFirstFree = entry;
     240          78 : }
     241             : 
     242             : } // namespace
     243             : 
     244             : void
     245           3 : nsTimerEvent::Init()
     246             : {
     247           3 :   sAllocator = new TimerEventAllocator();
     248           3 : }
     249             : 
     250             : void
     251           0 : nsTimerEvent::Shutdown()
     252             : {
     253           0 :   sCanDeleteAllocator = true;
     254           0 :   DeleteAllocatorIfNeeded();
     255           0 : }
     256             : 
     257             : void
     258          78 : nsTimerEvent::DeleteAllocatorIfNeeded()
     259             : {
     260          78 :   if (sCanDeleteAllocator && sAllocatorUsers == 0) {
     261           0 :     delete sAllocator;
     262           0 :     sAllocator = nullptr;
     263             :   }
     264          78 : }
     265             : 
     266             : NS_IMETHODIMP
     267          85 : nsTimerEvent::GetName(nsACString& aName)
     268             : {
     269             :   bool current;
     270          85 :   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mTimer->mEventTarget->IsOnCurrentThread(&current)) && current);
     271             : 
     272          85 :   mTimer->GetName(aName);
     273          85 :   return NS_OK;
     274             : }
     275             : 
     276             : NS_IMETHODIMP
     277          78 : nsTimerEvent::Run()
     278             : {
     279          78 :   if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
     280           0 :     TimeStamp now = TimeStamp::Now();
     281           0 :     MOZ_LOG(GetTimerLog(), LogLevel::Debug,
     282             :            ("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
     283             :             this, (now - mInitTime).ToMilliseconds()));
     284             :   }
     285             : 
     286          78 :   mTimer->Fire(mGeneration);
     287             : 
     288          78 :   return NS_OK;
     289             : }
     290             : 
     291             : nsresult
     292         306 : TimerThread::Init()
     293             : {
     294         306 :   mMonitor.AssertCurrentThreadOwns();
     295         306 :   MOZ_LOG(GetTimerLog(), LogLevel::Debug,
     296             :          ("TimerThread::Init [%d]\n", mInitialized));
     297             : 
     298         306 :   if (!mInitialized) {
     299           3 :     nsTimerEvent::Init();
     300             : 
     301             :     // We hold on to mThread to keep the thread alive.
     302             :     nsresult rv =
     303           3 :       NS_NewNamedThread("Timer Thread", getter_AddRefs(mThread), this);
     304           3 :     if (NS_FAILED(rv)) {
     305           0 :       mThread = nullptr;
     306             :     } else {
     307           6 :       RefPtr<TimerObserverRunnable> r = new TimerObserverRunnable(this);
     308           3 :       if (NS_IsMainThread()) {
     309           3 :         r->Run();
     310             :       } else {
     311           0 :         NS_DispatchToMainThread(r);
     312             :       }
     313             :     }
     314             : 
     315           3 :     mInitialized = true;
     316             :   }
     317             : 
     318         306 :   if (!mThread) {
     319           0 :     return NS_ERROR_FAILURE;
     320             :   }
     321             : 
     322         306 :   return NS_OK;
     323             : }
     324             : 
     325             : nsresult
     326           0 : TimerThread::Shutdown()
     327             : {
     328           0 :   MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown begin\n"));
     329             : 
     330           0 :   if (!mThread) {
     331           0 :     return NS_ERROR_NOT_INITIALIZED;
     332             :   }
     333             : 
     334           0 :   nsTArray<UniquePtr<Entry>> timers;
     335             :   {
     336             :     // lock scope
     337           0 :     MonitorAutoLock lock(mMonitor);
     338             : 
     339           0 :     mShutdown = true;
     340             : 
     341             :     // notify the cond var so that Run() can return
     342           0 :     if (mWaiting) {
     343           0 :       mNotified = true;
     344           0 :       mMonitor.Notify();
     345             :     }
     346             : 
     347             :     // Need to copy content of mTimers array to a local array
     348             :     // because call to timers' Cancel() (and release its self)
     349             :     // must not be done under the lock. Destructor of a callback
     350             :     // might potentially call some code reentering the same lock
     351             :     // that leads to unexpected behavior or deadlock.
     352             :     // See bug 422472.
     353           0 :     mTimers.SwapElements(timers);
     354             :   }
     355             : 
     356           0 :   uint32_t timersCount = timers.Length();
     357           0 :   for (uint32_t i = 0; i < timersCount; i++) {
     358           0 :     RefPtr<nsTimerImpl> timer = timers[i]->Take();
     359           0 :     if (timer) {
     360           0 :       timer->Cancel();
     361             :     }
     362             :   }
     363             : 
     364           0 :   mThread->Shutdown();    // wait for the thread to die
     365             : 
     366           0 :   nsTimerEvent::Shutdown();
     367             : 
     368           0 :   MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown end\n"));
     369           0 :   return NS_OK;
     370             : }
     371             : 
     372             : namespace {
     373             : 
     374             : struct MicrosecondsToInterval
     375             : {
     376          27 :   PRIntervalTime operator[](size_t aMs) const {
     377          27 :     return PR_MicrosecondsToInterval(aMs);
     378             :   }
     379             : };
     380             : 
     381             : struct IntervalComparator
     382             : {
     383          27 :   int operator()(PRIntervalTime aInterval) const {
     384          27 :     return (0 < aInterval) ? -1 : 1;
     385             :   }
     386             : };
     387             : 
     388             : } // namespace
     389             : 
     390             : NS_IMETHODIMP
     391           3 : TimerThread::Run()
     392             : {
     393           3 :   NS_SetCurrentThreadName("Timer");
     394             : 
     395           3 :   MonitorAutoLock lock(mMonitor);
     396             : 
     397             :   // We need to know how many microseconds give a positive PRIntervalTime. This
     398             :   // is platform-dependent and we calculate it at runtime, finding a value |v|
     399             :   // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
     400             :   // the range [0, v) to find the ms-to-interval scale.
     401           3 :   uint32_t usForPosInterval = 1;
     402          57 :   while (PR_MicrosecondsToInterval(usForPosInterval) == 0) {
     403          27 :     usForPosInterval <<= 1;
     404             :   }
     405             : 
     406             :   size_t usIntervalResolution;
     407           3 :   BinarySearchIf(MicrosecondsToInterval(), 0, usForPosInterval, IntervalComparator(), &usIntervalResolution);
     408           3 :   MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution - 1) == 0);
     409           3 :   MOZ_ASSERT(PR_MicrosecondsToInterval(usIntervalResolution) == 1);
     410             : 
     411             :   // Half of the amount of microseconds needed to get positive PRIntervalTime.
     412             :   // We use this to decide how to round our wait times later
     413           3 :   mAllowedEarlyFiringMicroseconds = usIntervalResolution / 2;
     414           3 :   bool forceRunNextTimer = false;
     415             : 
     416         607 :   while (!mShutdown) {
     417             :     // Have to use PRIntervalTime here, since PR_WaitCondVar takes it
     418             :     PRIntervalTime waitFor;
     419         305 :     bool forceRunThisTimer = forceRunNextTimer;
     420         305 :     forceRunNextTimer = false;
     421             : 
     422         305 :     if (mSleeping) {
     423             :       // Sleep for 0.1 seconds while not firing timers.
     424           0 :       uint32_t milliseconds = 100;
     425           0 :       if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
     426           0 :         milliseconds = ChaosMode::randomUint32LessThan(200);
     427             :       }
     428           0 :       waitFor = PR_MillisecondsToInterval(milliseconds);
     429             :     } else {
     430         305 :       waitFor = PR_INTERVAL_NO_TIMEOUT;
     431         305 :       TimeStamp now = TimeStamp::Now();
     432             : 
     433         305 :       RemoveLeadingCanceledTimersInternal();
     434             : 
     435         305 :       if (!mTimers.IsEmpty()) {
     436         304 :         if (now >= mTimers[0]->Value()->mTimeout || forceRunThisTimer) {
     437             :     next:
     438             :           // NB: AddRef before the Release under RemoveTimerInternal to avoid
     439             :           // mRefCnt passing through zero, in case all other refs than the one
     440             :           // from mTimers have gone away (the last non-mTimers[i]-ref's Release
     441             :           // must be racing with us, blocked in gThread->RemoveTimer waiting
     442             :           // for TimerThread::mMonitor, under nsTimerImpl::Release.
     443             : 
     444         180 :           RefPtr<nsTimerImpl> timerRef(mTimers[0]->Take());
     445          90 :           RemoveFirstTimerInternal();
     446             : 
     447          90 :           MOZ_LOG(GetTimerLog(), LogLevel::Debug,
     448             :                  ("Timer thread woke up %fms from when it was supposed to\n",
     449             :                   fabs((now - timerRef->mTimeout).ToMilliseconds())));
     450             : 
     451             :           // We are going to let the call to PostTimerEvent here handle the
     452             :           // release of the timer so that we don't end up releasing the timer
     453             :           // on the TimerThread instead of on the thread it targets.
     454          90 :           timerRef = PostTimerEvent(timerRef.forget());
     455             : 
     456          90 :           if (timerRef) {
     457             :             // We got our reference back due to an error.
     458             :             // Unhook the nsRefPtr, and release manually so we can get the
     459             :             // refcount.
     460           0 :             nsrefcnt rc = timerRef.forget().take()->Release();
     461             :             (void)rc;
     462             : 
     463             :             // The nsITimer interface requires that its users keep a reference
     464             :             // to the timers they use while those timers are initialized but
     465             :             // have not yet fired.  If this ever happens, it is a bug in the
     466             :             // code that created and used the timer.
     467             :             //
     468             :             // Further, note that this should never happen even with a
     469             :             // misbehaving user, because nsTimerImpl::Release checks for a
     470             :             // refcount of 1 with an armed timer (a timer whose only reference
     471             :             // is from the timer thread) and when it hits this will remove the
     472             :             // timer from the timer thread and thus destroy the last reference,
     473             :             // preventing this situation from occurring.
     474           0 :             MOZ_ASSERT(rc != 0, "destroyed timer off its target thread!");
     475             :           }
     476             : 
     477          90 :           if (mShutdown) {
     478           0 :             break;
     479             :           }
     480             : 
     481             :           // Update now, as PostTimerEvent plus the locking may have taken a
     482             :           // tick or two, and we may goto next below.
     483          90 :           now = TimeStamp::Now();
     484             :         }
     485             :       }
     486             : 
     487         322 :       RemoveLeadingCanceledTimersInternal();
     488             : 
     489         322 :       if (!mTimers.IsEmpty()) {
     490         321 :         TimeStamp timeout = mTimers[0]->Value()->mTimeout;
     491             : 
     492             :         // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer
     493             :         // is due now or overdue.
     494             :         //
     495             :         // Note that we can only sleep for integer values of a certain
     496             :         // resolution. We use mAllowedEarlyFiringMicroseconds, calculated
     497             :         // before, to do the optimal rounding (i.e., of how to decide what
     498             :         // interval is so small we should not wait at all).
     499         321 :         double microseconds = (timeout - now).ToMilliseconds() * 1000;
     500             : 
     501         321 :         if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
     502             :           // The mean value of sFractions must be 1 to ensure that
     503             :           // the average of a long sequence of timeouts converges to the
     504             :           // actual sum of their times.
     505             :           static const float sFractions[] = {
     506             :             0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.75f, 2.75f
     507             :           };
     508           0 :           microseconds *=
     509           0 :             sFractions[ChaosMode::randomUint32LessThan(ArrayLength(sFractions))];
     510           0 :           forceRunNextTimer = true;
     511             :         }
     512             : 
     513         321 :         if (microseconds < mAllowedEarlyFiringMicroseconds) {
     514          17 :           forceRunNextTimer = false;
     515          17 :           goto next; // round down; execute event now
     516             :         }
     517         304 :         waitFor = PR_MicrosecondsToInterval(
     518         304 :           static_cast<uint32_t>(microseconds)); // Floor is accurate enough.
     519         304 :         if (waitFor == 0) {
     520          11 :           waitFor = 1;  // round up, wait the minimum time we can wait
     521             :         }
     522             :       }
     523             : 
     524         305 :       if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
     525           0 :         if (waitFor == PR_INTERVAL_NO_TIMEOUT)
     526           0 :           MOZ_LOG(GetTimerLog(), LogLevel::Debug,
     527             :                  ("waiting for PR_INTERVAL_NO_TIMEOUT\n"));
     528             :         else
     529           0 :           MOZ_LOG(GetTimerLog(), LogLevel::Debug,
     530             :                  ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor)));
     531             :       }
     532             :     }
     533             : 
     534         305 :     mWaiting = true;
     535         305 :     mNotified = false;
     536         305 :     mMonitor.Wait(waitFor);
     537         302 :     if (mNotified) {
     538         208 :       forceRunNextTimer = false;
     539             :     }
     540         302 :     mWaiting = false;
     541             :   }
     542             : 
     543           0 :   return NS_OK;
     544             : }
     545             : 
     546             : nsresult
     547         306 : TimerThread::AddTimer(nsTimerImpl* aTimer)
     548             : {
     549         612 :   MonitorAutoLock lock(mMonitor);
     550             : 
     551         306 :   if (!aTimer->mEventTarget) {
     552           0 :     return NS_ERROR_NOT_INITIALIZED;
     553             :   }
     554             : 
     555         306 :   nsresult rv = Init();
     556         306 :   if (NS_FAILED(rv)) {
     557           0 :     return rv;
     558             :   }
     559             : 
     560             :   // Add the timer to our list.
     561         306 :   if(!AddTimerInternal(aTimer)) {
     562           0 :     return NS_ERROR_OUT_OF_MEMORY;
     563             :   }
     564             : 
     565             :   // Awaken the timer thread.
     566         306 :   if (mWaiting && mTimers[0]->Value() == aTimer) {
     567          36 :     mNotified = true;
     568          36 :     mMonitor.Notify();
     569             :   }
     570             : 
     571         306 :   return NS_OK;
     572             : }
     573             : 
     574             : nsresult
     575        1714 : TimerThread::RemoveTimer(nsTimerImpl* aTimer)
     576             : {
     577        3428 :   MonitorAutoLock lock(mMonitor);
     578             : 
     579             :   // Remove the timer from our array.  Tell callers that aTimer was not found
     580             :   // by returning NS_ERROR_NOT_AVAILABLE.
     581             : 
     582        1714 :   if (!RemoveTimerInternal(aTimer)) {
     583        1540 :     return NS_ERROR_NOT_AVAILABLE;
     584             :   }
     585             : 
     586             :   // Awaken the timer thread.
     587         174 :   if (mWaiting) {
     588         173 :     mNotified = true;
     589         173 :     mMonitor.Notify();
     590             :   }
     591             : 
     592         174 :   return NS_OK;
     593             : }
     594             : 
     595             : TimeStamp
     596         159 : TimerThread::FindNextFireTimeForCurrentThread(TimeStamp aDefault, uint32_t aSearchBound)
     597             : {
     598         318 :   MonitorAutoLock lock(mMonitor);
     599         159 :   TimeStamp timeStamp = aDefault;
     600         159 :   uint32_t index = 0;
     601             : 
     602             : #ifdef DEBUG
     603         159 :   TimeStamp firstTimeStamp;
     604         159 :   Entry* initialFirstEntry = nullptr;
     605         159 :   if (!mTimers.IsEmpty()) {
     606         159 :     initialFirstEntry = mTimers[0].get();
     607         159 :     firstTimeStamp = mTimers[0]->Timeout();
     608             :   }
     609             : #endif
     610             : 
     611         159 :   auto end = mTimers.end();
     612         163 :   while(end != mTimers.begin()) {
     613         161 :     nsTimerImpl* timer = mTimers[0]->Value();
     614         161 :     if (timer) {
     615         161 :       if (timer->mTimeout > aDefault) {
     616         155 :         timeStamp = aDefault;
     617         155 :         break;
     618             :       }
     619             : 
     620             :       // Don't yield to timers created with the *_LOW_PRIORITY type.
     621           6 :       if (!timer->IsLowPriority()) {
     622           4 :         bool isOnCurrentThread = false;
     623           4 :         nsresult rv = timer->mEventTarget->IsOnCurrentThread(&isOnCurrentThread);
     624           4 :         if (NS_SUCCEEDED(rv) && isOnCurrentThread) {
     625           4 :           timeStamp = timer->mTimeout;
     626           4 :           break;
     627             :         }
     628             :       }
     629             : 
     630           2 :       if (++index > aSearchBound) {
     631             :         // Track the currently highest timeout so that we can bail out when we
     632             :         // reach the bound or when we find a timer for the current thread.
     633             :         // This won't give accurate information if we stop before finding
     634             :         // any timer for the current thread, but at least won't report too
     635             :         // long idle period.
     636           0 :         timeStamp = timer->mTimeout;
     637           0 :         break;
     638             :       }
     639             :     }
     640             : 
     641           2 :     std::pop_heap(mTimers.begin(), end, Entry::UniquePtrLessThan);
     642           2 :     --end;
     643             :   }
     644             : 
     645         163 :   while (end != mTimers.end()) {
     646           2 :     ++end;
     647           2 :     std::push_heap(mTimers.begin(), end, Entry::UniquePtrLessThan);
     648             :   }
     649             : 
     650             : #ifdef DEBUG
     651         159 :   if (!mTimers.IsEmpty()) {
     652         159 :     if (firstTimeStamp != mTimers[0]->Timeout()) {
     653           0 :       TimeStamp now = TimeStamp::Now();
     654           0 :       printf_stderr("firstTimeStamp %f, mTimers[0]->Timeout() %f, "
     655             :                     "initialFirstTimer %p, current first %p\n",
     656           0 :                     (firstTimeStamp - now).ToMilliseconds(),
     657           0 :                     (mTimers[0]->Timeout() - now).ToMilliseconds(),
     658           0 :                     initialFirstEntry, mTimers[0].get());
     659             :     }
     660             :   }
     661         159 :   MOZ_ASSERT_IF(!mTimers.IsEmpty(), firstTimeStamp == mTimers[0]->Timeout());
     662             : #endif
     663             : 
     664         318 :   return timeStamp;
     665             : }
     666             : 
     667             : // This function must be called from within a lock
     668             : bool
     669         306 : TimerThread::AddTimerInternal(nsTimerImpl* aTimer)
     670             : {
     671         306 :   mMonitor.AssertCurrentThreadOwns();
     672         306 :   if (mShutdown) {
     673           0 :     return false;
     674             :   }
     675             : 
     676         306 :   TimeStamp now = TimeStamp::Now();
     677             : 
     678         306 :   UniquePtr<Entry>* entry = mTimers.AppendElement(
     679         612 :     MakeUnique<Entry>(now, aTimer->mTimeout, aTimer), mozilla::fallible);
     680         306 :   if (!entry) {
     681           0 :     return false;
     682             :   }
     683             : 
     684         306 :   std::push_heap(mTimers.begin(), mTimers.end(), Entry::UniquePtrLessThan);
     685             : 
     686             : #ifdef MOZ_TASK_TRACER
     687             :   // Caller of AddTimer is the parent task of its timer event, so we store the
     688             :   // TraceInfo here for later used.
     689             :   aTimer->GetTLSTraceInfo();
     690             : #endif
     691             : 
     692         306 :   return true;
     693             : }
     694             : 
     695             : bool
     696        1714 : TimerThread::RemoveTimerInternal(nsTimerImpl* aTimer)
     697             : {
     698        1714 :   mMonitor.AssertCurrentThreadOwns();
     699        1714 :   if (!aTimer || !aTimer->mHolder) {
     700        1540 :     return false;
     701             :   }
     702         174 :   aTimer->mHolder->Forget(aTimer);
     703         174 :   return true;
     704             : }
     705             : 
     706             : void
     707         627 : TimerThread::RemoveLeadingCanceledTimersInternal()
     708             : {
     709         627 :   mMonitor.AssertCurrentThreadOwns();
     710             : 
     711             :   // Move all canceled timers from the front of the list to
     712             :   // the back of the list using std::pop_heap().  We do this
     713             :   // without actually removing them from the list so we can
     714             :   // modify the nsTArray in a single bulk operation.
     715         627 :   auto sortedEnd = mTimers.end();
     716         807 :   while (sortedEnd != mTimers.begin() && !mTimers[0]->Value()) {
     717          90 :     std::pop_heap(mTimers.begin(), sortedEnd, Entry::UniquePtrLessThan);
     718          90 :     --sortedEnd;
     719             :   }
     720             : 
     721             :   // If there were no canceled timers then we are done.
     722         627 :   if (sortedEnd == mTimers.end()) {
     723         591 :     return;
     724             :   }
     725             : 
     726             :   // Finally, remove the canceled timers from the back of the
     727             :   // nsTArray.  Note, since std::pop_heap() uses iterators
     728             :   // we must convert to nsTArray indices and number of
     729             :   // elements here.
     730          72 :   mTimers.RemoveElementsAt(sortedEnd - mTimers.begin(),
     731         108 :                            mTimers.end() - sortedEnd);
     732             : }
     733             : 
     734             : void
     735          90 : TimerThread::RemoveFirstTimerInternal()
     736             : {
     737          90 :   mMonitor.AssertCurrentThreadOwns();
     738          90 :   MOZ_ASSERT(!mTimers.IsEmpty());
     739          90 :   std::pop_heap(mTimers.begin(), mTimers.end(), Entry::UniquePtrLessThan);
     740          90 :   mTimers.RemoveElementAt(mTimers.Length() - 1);
     741          90 : }
     742             : 
     743             : already_AddRefed<nsTimerImpl>
     744          90 : TimerThread::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
     745             : {
     746          90 :   mMonitor.AssertCurrentThreadOwns();
     747             : 
     748         180 :   RefPtr<nsTimerImpl> timer(aTimerRef);
     749          90 :   if (!timer->mEventTarget) {
     750           0 :     NS_ERROR("Attempt to post timer event to NULL event target");
     751           0 :     return timer.forget();
     752             :   }
     753             : 
     754             :   // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
     755             : 
     756             :   // Since we already addref'd 'timer', we don't need to addref here.
     757             :   // We will release either in ~nsTimerEvent(), or pass the reference back to
     758             :   // the caller. We need to copy the generation number from this timer into the
     759             :   // event, so we can avoid firing a timer that was re-initialized after being
     760             :   // canceled.
     761             : 
     762         180 :   RefPtr<nsTimerEvent> event = new nsTimerEvent;
     763          90 :   if (!event) {
     764           0 :     return timer.forget();
     765             :   }
     766             : 
     767          90 :   if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
     768           0 :     event->mInitTime = TimeStamp::Now();
     769             :   }
     770             : 
     771             : #ifdef MOZ_TASK_TRACER
     772             :   // During the dispatch of TimerEvent, we overwrite the current TraceInfo
     773             :   // partially with the info saved in timer earlier, and restore it back by
     774             :   // AutoSaveCurTraceInfo.
     775             :   AutoSaveCurTraceInfo saveCurTraceInfo;
     776             :   (timer->GetTracedTask()).SetTLSTraceInfo();
     777             : #endif
     778             : 
     779         180 :   nsCOMPtr<nsIEventTarget> target = timer->mEventTarget;
     780          90 :   event->SetTimer(timer.forget());
     781             : 
     782             :   nsresult rv;
     783             :   {
     784             :     // We release mMonitor around the Dispatch because if this timer is targeted
     785             :     // at the TimerThread we'll deadlock.
     786         180 :     MonitorAutoUnlock unlock(mMonitor);
     787          90 :     rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
     788             :   }
     789             : 
     790          90 :   if (NS_FAILED(rv)) {
     791           0 :     timer = event->ForgetTimer();
     792           0 :     RemoveTimerInternal(timer);
     793           0 :     return timer.forget();
     794             :   }
     795             : 
     796          90 :   return nullptr;
     797             : }
     798             : 
     799             : void
     800           0 : TimerThread::DoBeforeSleep()
     801             : {
     802             :   // Mainthread
     803           0 :   MonitorAutoLock lock(mMonitor);
     804           0 :   mSleeping = true;
     805           0 : }
     806             : 
     807             : // Note: wake may be notified without preceding sleep notification
     808             : void
     809           0 : TimerThread::DoAfterSleep()
     810             : {
     811             :   // Mainthread
     812           0 :   MonitorAutoLock lock(mMonitor);
     813           0 :   mSleeping = false;
     814             : 
     815             :   // Wake up the timer thread to re-process the array to ensure the sleep delay is correct,
     816             :   // and fire any expired timers (perhaps quite a few)
     817           0 :   mNotified = true;
     818           0 :   mMonitor.Notify();
     819           0 : }
     820             : 
     821             : 
     822             : NS_IMETHODIMP
     823           0 : TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
     824             :                      const char16_t* /* aData */)
     825             : {
     826           0 :   if (strcmp(aTopic, "sleep_notification") == 0 ||
     827           0 :       strcmp(aTopic, "suspend_process_notification") == 0) {
     828           0 :     DoBeforeSleep();
     829           0 :   } else if (strcmp(aTopic, "wake_notification") == 0 ||
     830           0 :              strcmp(aTopic, "resume_process_notification") == 0) {
     831           0 :     DoAfterSleep();
     832             :   }
     833             : 
     834           0 :   return NS_OK;
     835             : }
     836             : 
     837             : uint32_t
     838           2 : TimerThread::AllowedEarlyFiringMicroseconds() const
     839             : {
     840           2 :   return mAllowedEarlyFiringMicroseconds;
     841             : }

Generated by: LCOV version 1.13