forked from mirrors/gecko-dev
		
	 4a6beb6b60
			
		
	
	
		4a6beb6b60
		
	
	
	
	
		
			
			Initially controlled by a pref and only enabled by default in Nightly. Differential Revision: https://phabricator.services.mozilla.com/D189555
		
			
				
	
	
		
			247 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef TimerThread_h___
 | |
| #define TimerThread_h___
 | |
| 
 | |
| #include "nsIObserver.h"
 | |
| #include "nsIRunnable.h"
 | |
| #include "nsIThread.h"
 | |
| 
 | |
| #include "nsTimerImpl.h"
 | |
| #include "nsThreadUtils.h"
 | |
| 
 | |
| #include "nsTArray.h"
 | |
| 
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/HalTypes.h"
 | |
| #include "mozilla/Monitor.h"
 | |
| #include "mozilla/ProfilerUtils.h"
 | |
| 
 | |
| // Enable this to compute lots of interesting statistics and print them out when
 | |
| // PrintStatistics() is called.
 | |
| #define TIMER_THREAD_STATISTICS 0
 | |
| 
 | |
| class TimerThread final : public mozilla::Runnable, public nsIObserver {
 | |
|  public:
 | |
|   typedef mozilla::Monitor Monitor;
 | |
|   typedef mozilla::MutexAutoLock MutexAutoLock;
 | |
|   typedef mozilla::TimeStamp TimeStamp;
 | |
|   typedef mozilla::TimeDuration TimeDuration;
 | |
| 
 | |
|   TimerThread();
 | |
| 
 | |
|   NS_DECL_ISUPPORTS_INHERITED
 | |
|   NS_DECL_NSIRUNNABLE
 | |
|   NS_DECL_NSIOBSERVER
 | |
| 
 | |
|   nsresult Shutdown();
 | |
| 
 | |
|   nsresult AddTimer(nsTimerImpl* aTimer, const MutexAutoLock& aProofOfLock)
 | |
|       MOZ_REQUIRES(aTimer->mMutex);
 | |
|   nsresult RemoveTimer(nsTimerImpl* aTimer, const MutexAutoLock& aProofOfLock)
 | |
|       MOZ_REQUIRES(aTimer->mMutex);
 | |
|   // Considering only the first 'aSearchBound' timers (in firing order), returns
 | |
|   // the timeout of the first non-low-priority timer, on the current thread,
 | |
|   // that will fire before 'aDefault'. If no such timer exists, 'aDefault' is
 | |
|   // returned.
 | |
|   TimeStamp FindNextFireTimeForCurrentThread(TimeStamp aDefault,
 | |
|                                              uint32_t aSearchBound);
 | |
| 
 | |
|   void DoBeforeSleep();
 | |
|   void DoAfterSleep();
 | |
| 
 | |
|   bool IsOnTimerThread() const { return mThread->IsOnCurrentThread(); }
 | |
| 
 | |
|   uint32_t AllowedEarlyFiringMicroseconds();
 | |
|   nsresult GetTimers(nsTArray<RefPtr<nsITimer>>& aRetVal);
 | |
| 
 | |
|  private:
 | |
|   ~TimerThread();
 | |
| 
 | |
|   bool mInitialized;
 | |
| 
 | |
|   // These internal helper methods must be called while mMonitor is held.
 | |
|   // AddTimerInternal returns false if the insertion failed.
 | |
|   bool AddTimerInternal(nsTimerImpl& aTimer) MOZ_REQUIRES(mMonitor);
 | |
|   bool RemoveTimerInternal(nsTimerImpl& aTimer)
 | |
|       MOZ_REQUIRES(mMonitor, aTimer.mMutex);
 | |
|   void RemoveLeadingCanceledTimersInternal() MOZ_REQUIRES(mMonitor);
 | |
|   void RemoveFirstTimerInternal() MOZ_REQUIRES(mMonitor);
 | |
|   nsresult Init() MOZ_REQUIRES(mMonitor);
 | |
| 
 | |
|   void PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
 | |
|       MOZ_REQUIRES(mMonitor);
 | |
| 
 | |
|   // Using atomic because this value is written to in one place, and read from
 | |
|   // in another, and those two locations are likely to be executed from separate
 | |
|   // threads. Reads/writes to an aligned value this size should be atomic even
 | |
|   // without using std::atomic, but doing this explicitly provides a good
 | |
|   // reminder that this is accessed from multiple threads.
 | |
|   std::atomic<mozilla::hal::ProcessPriority> mCachedPriority =
 | |
|       mozilla::hal::PROCESS_PRIORITY_UNKNOWN;
 | |
| 
 | |
|   nsCOMPtr<nsIThread> mThread;
 | |
|   // Lock ordering requirements:
 | |
|   // (optional) ThreadWrapper::sMutex ->
 | |
|   // (optional) nsTimerImpl::mMutex   ->
 | |
|   // TimerThread::mMonitor
 | |
|   Monitor mMonitor;
 | |
| 
 | |
|   bool mShutdown MOZ_GUARDED_BY(mMonitor);
 | |
|   bool mWaiting MOZ_GUARDED_BY(mMonitor);
 | |
|   bool mNotified MOZ_GUARDED_BY(mMonitor);
 | |
|   bool mSleeping MOZ_GUARDED_BY(mMonitor);
 | |
| 
 | |
|   class Entry final {
 | |
|    public:
 | |
|     explicit Entry(nsTimerImpl& aTimerImpl)
 | |
|         : mTimeout(aTimerImpl.mTimeout),
 | |
|           mDelay(aTimerImpl.mDelay),
 | |
|           mTimerImpl(&aTimerImpl) {
 | |
|       aTimerImpl.SetIsInTimerThread(true);
 | |
|     }
 | |
| 
 | |
|     // Create an already-canceled entry with the given timeout.
 | |
|     explicit Entry(TimeStamp aTimeout)
 | |
|         : mTimeout(std::move(aTimeout)), mTimerImpl(nullptr) {}
 | |
| 
 | |
|     // Don't allow copies, otherwise which one would manage `IsInTimerThread`?
 | |
|     Entry(const Entry&) = delete;
 | |
|     Entry& operator=(const Entry&) = delete;
 | |
| 
 | |
|     // Move-only.
 | |
|     Entry(Entry&&) = default;
 | |
|     Entry& operator=(Entry&&) = default;
 | |
| 
 | |
|     ~Entry() {
 | |
|       if (mTimerImpl) {
 | |
|         mTimerImpl->mMutex.AssertCurrentThreadOwns();
 | |
|         mTimerImpl->SetIsInTimerThread(false);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     nsTimerImpl* Value() const { return mTimerImpl; }
 | |
| 
 | |
|     void Forget() {
 | |
|       if (MOZ_UNLIKELY(!mTimerImpl)) {
 | |
|         return;
 | |
|       }
 | |
|       mTimerImpl->mMutex.AssertCurrentThreadOwns();
 | |
|       mTimerImpl->SetIsInTimerThread(false);
 | |
|       mTimerImpl = nullptr;
 | |
|     }
 | |
| 
 | |
|     // Called with the Monitor held, but not the TimerImpl's mutex
 | |
|     already_AddRefed<nsTimerImpl> Take() {
 | |
|       if (MOZ_LIKELY(mTimerImpl)) {
 | |
|         MOZ_ASSERT(mTimerImpl->IsInTimerThread());
 | |
|         mTimerImpl->SetIsInTimerThread(false);
 | |
|       }
 | |
|       return mTimerImpl.forget();
 | |
|     }
 | |
| 
 | |
|     const TimeStamp& Timeout() const { return mTimeout; }
 | |
|     const TimeDuration& Delay() const { return mDelay; }
 | |
| 
 | |
|    private:
 | |
|     // These values are simply cached from the timer. Keeping them here is good
 | |
|     // for cache usage and allows us to avoid worrying about locking conflicts
 | |
|     // with the timer.
 | |
|     TimeStamp mTimeout;
 | |
|     TimeDuration mDelay;
 | |
| 
 | |
|     RefPtr<nsTimerImpl> mTimerImpl;
 | |
|   };
 | |
| 
 | |
|   // Computes and returns the index in mTimers at which a new timer with the
 | |
|   // specified timeout should be inserted in order to maintain "sorted" order.
 | |
|   size_t ComputeTimerInsertionIndex(const TimeStamp& timeout) const
 | |
|       MOZ_REQUIRES(mMonitor);
 | |
| 
 | |
|   // Computes and returns when we should next try to wake up in order to handle
 | |
|   // the triggering of the timers in mTimers. Currently this is very simple and
 | |
|   // we always just plan to wake up for the next timer in the list. In the
 | |
|   // future this will be more sophisticated.
 | |
|   TimeStamp ComputeWakeupTimeFromTimers() const MOZ_REQUIRES(mMonitor);
 | |
| 
 | |
|   // Computes how late a timer can acceptably fire.
 | |
|   // timerDuration is the duration of the timer whose delay we are calculating.
 | |
|   // Longer timers can tolerate longer firing delays.
 | |
|   // minDelay is an amount by which any timer can be delayed.
 | |
|   // This function will never return a value smaller than minDelay (unless this
 | |
|   // conflicts with maxDelay). maxDelay is the upper limit on the amount by
 | |
|   // which we will ever delay any timer. Takes precedence over minDelay if there
 | |
|   // is a conflict. (Zero will effectively disable timer coalescing.)
 | |
|   TimeDuration ComputeAcceptableFiringDelay(TimeDuration timerDuration,
 | |
|                                             TimeDuration minDelay,
 | |
|                                             TimeDuration maxDelay) const;
 | |
| 
 | |
| #ifdef XP_WIN
 | |
|   UINT ComputeDesiredTimerPeriod() const;
 | |
| #endif
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   // Checks mTimers to see if any entries are out of order or any cached
 | |
|   // timeouts are incorrect and will assert if any inconsistency is found. Has
 | |
|   // no side effects other than asserting so has no use in non-DEBUG builds.
 | |
|   void VerifyTimerListConsistency() const MOZ_REQUIRES(mMonitor);
 | |
| #endif
 | |
| 
 | |
|   // mTimers is maintained in a "pseudo-sorted" order wrt the timeouts.
 | |
|   // Specifcally, mTimers is sorted according to the timeouts *if you ignore the
 | |
|   // canceled entries* (those whose mTimerImpl is nullptr). Notably this means
 | |
|   // that you cannot use a binary search on this list.
 | |
|   nsTArray<Entry> mTimers MOZ_GUARDED_BY(mMonitor);
 | |
| 
 | |
|   // Set only at the start of the thread's Run():
 | |
|   uint32_t mAllowedEarlyFiringMicroseconds MOZ_GUARDED_BY(mMonitor);
 | |
| 
 | |
|   ProfilerThreadId mProfilerThreadId MOZ_GUARDED_BY(mMonitor);
 | |
| 
 | |
|   // Time at which we were intending to wake up the last time that we slept.
 | |
|   // Is "null" if we have never slept or if our last sleep was "forever".
 | |
|   TimeStamp mIntendedWakeupTime;
 | |
| 
 | |
| #if TIMER_THREAD_STATISTICS
 | |
|   static constexpr size_t sTimersFiredPerWakeupBucketCount = 16;
 | |
|   static inline constexpr std::array<size_t, sTimersFiredPerWakeupBucketCount>
 | |
|       sTimersFiredPerWakeupThresholds = {
 | |
|           0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 20, 30, 40, 50, 70, (size_t)(-1)};
 | |
| 
 | |
|   mutable AutoTArray<size_t, sTimersFiredPerWakeupBucketCount>
 | |
|       mTimersFiredPerWakeup MOZ_GUARDED_BY(mMonitor) = {0, 0, 0, 0, 0, 0, 0, 0,
 | |
|                                                         0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   mutable AutoTArray<size_t, sTimersFiredPerWakeupBucketCount>
 | |
|       mTimersFiredPerUnnotifiedWakeup MOZ_GUARDED_BY(mMonitor) = {
 | |
|           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
|   mutable AutoTArray<size_t, sTimersFiredPerWakeupBucketCount>
 | |
|       mTimersFiredPerNotifiedWakeup MOZ_GUARDED_BY(mMonitor) = {
 | |
|           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | |
| 
 | |
|   mutable size_t mTotalTimersAdded MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable size_t mTotalTimersRemoved MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable size_t mTotalTimersFiredNotified MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable size_t mTotalTimersFiredUnnotified MOZ_GUARDED_BY(mMonitor) = 0;
 | |
| 
 | |
|   mutable size_t mTotalWakeupCount MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable size_t mTotalUnnotifiedWakeupCount MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable size_t mTotalNotifiedWakeupCount MOZ_GUARDED_BY(mMonitor) = 0;
 | |
| 
 | |
|   mutable double mTotalActualTimerFiringDelayNotified MOZ_GUARDED_BY(mMonitor) =
 | |
|       0.0;
 | |
|   mutable double mTotalActualTimerFiringDelayUnnotified
 | |
|       MOZ_GUARDED_BY(mMonitor) = 0.0;
 | |
| 
 | |
|   mutable TimeStamp mFirstTimerAdded MOZ_GUARDED_BY(mMonitor);
 | |
| 
 | |
|   mutable size_t mEarlyWakeups MOZ_GUARDED_BY(mMonitor) = 0;
 | |
|   mutable double mTotalEarlyWakeupTime MOZ_GUARDED_BY(mMonitor) = 0.0;
 | |
| 
 | |
|   void PrintStatistics() const;
 | |
| #endif
 | |
| };
 | |
| #endif /* TimerThread_h___ */
 |