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/. */
7 #ifndef TimerThread_h___
8 #define TimerThread_h___
10 #include "nsIObserver.h"
11 #include "nsIRunnable.h"
12 #include "nsIThread.h"
14 #include "nsTimerImpl.h"
15 #include "nsThreadUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/HalTypes.h"
21 #include "mozilla/Monitor.h"
22 #include "mozilla/ProfilerUtils.h"
24 // Enable this to compute lots of interesting statistics and print them out when
25 // PrintStatistics() is called.
26 #define TIMER_THREAD_STATISTICS 0
28 class TimerThread final
: public mozilla::Runnable
, public nsIObserver
{
30 typedef mozilla::Monitor Monitor
;
31 typedef mozilla::MutexAutoLock MutexAutoLock
;
32 typedef mozilla::TimeStamp TimeStamp
;
33 typedef mozilla::TimeDuration TimeDuration
;
37 NS_DECL_ISUPPORTS_INHERITED
43 nsresult
AddTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
)
44 MOZ_REQUIRES(aTimer
->mMutex
);
45 nsresult
RemoveTimer(nsTimerImpl
* aTimer
, const MutexAutoLock
& aProofOfLock
)
46 MOZ_REQUIRES(aTimer
->mMutex
);
47 // Considering only the first 'aSearchBound' timers (in firing order), returns
48 // the timeout of the first non-low-priority timer, on the current thread,
49 // that will fire before 'aDefault'. If no such timer exists, 'aDefault' is
51 TimeStamp
FindNextFireTimeForCurrentThread(TimeStamp aDefault
,
52 uint32_t aSearchBound
);
57 bool IsOnTimerThread() const { return mThread
->IsOnCurrentThread(); }
59 uint32_t AllowedEarlyFiringMicroseconds();
60 nsresult
GetTimers(nsTArray
<RefPtr
<nsITimer
>>& aRetVal
);
67 // These internal helper methods must be called while mMonitor is held.
68 // AddTimerInternal returns false if the insertion failed.
69 bool AddTimerInternal(nsTimerImpl
& aTimer
) MOZ_REQUIRES(mMonitor
);
70 bool RemoveTimerInternal(nsTimerImpl
& aTimer
)
71 MOZ_REQUIRES(mMonitor
, aTimer
.mMutex
);
72 void RemoveLeadingCanceledTimersInternal() MOZ_REQUIRES(mMonitor
);
73 void RemoveFirstTimerInternal() MOZ_REQUIRES(mMonitor
);
74 nsresult
Init() MOZ_REQUIRES(mMonitor
);
76 void PostTimerEvent(already_AddRefed
<nsTimerImpl
> aTimerRef
)
77 MOZ_REQUIRES(mMonitor
);
79 // Using atomic because this value is written to in one place, and read from
80 // in another, and those two locations are likely to be executed from separate
81 // threads. Reads/writes to an aligned value this size should be atomic even
82 // without using std::atomic, but doing this explicitly provides a good
83 // reminder that this is accessed from multiple threads.
84 std::atomic
<mozilla::hal::ProcessPriority
> mCachedPriority
=
85 mozilla::hal::PROCESS_PRIORITY_UNKNOWN
;
87 nsCOMPtr
<nsIThread
> mThread
;
88 // Lock ordering requirements:
89 // (optional) ThreadWrapper::sMutex ->
90 // (optional) nsTimerImpl::mMutex ->
91 // TimerThread::mMonitor
94 bool mShutdown
MOZ_GUARDED_BY(mMonitor
);
95 bool mWaiting
MOZ_GUARDED_BY(mMonitor
);
96 bool mNotified
MOZ_GUARDED_BY(mMonitor
);
97 bool mSleeping
MOZ_GUARDED_BY(mMonitor
);
101 explicit Entry(nsTimerImpl
& aTimerImpl
)
102 : mTimeout(aTimerImpl
.mTimeout
),
103 mDelay(aTimerImpl
.mDelay
),
104 mTimerImpl(&aTimerImpl
) {
105 aTimerImpl
.SetIsInTimerThread(true);
108 // Create an already-canceled entry with the given timeout.
109 explicit Entry(TimeStamp aTimeout
)
110 : mTimeout(std::move(aTimeout
)), mTimerImpl(nullptr) {}
112 // Don't allow copies, otherwise which one would manage `IsInTimerThread`?
113 Entry(const Entry
&) = delete;
114 Entry
& operator=(const Entry
&) = delete;
117 Entry(Entry
&&) = default;
118 Entry
& operator=(Entry
&&) = default;
122 mTimerImpl
->mMutex
.AssertCurrentThreadOwns();
123 mTimerImpl
->SetIsInTimerThread(false);
127 nsTimerImpl
* Value() const { return mTimerImpl
; }
130 if (MOZ_UNLIKELY(!mTimerImpl
)) {
133 mTimerImpl
->mMutex
.AssertCurrentThreadOwns();
134 mTimerImpl
->SetIsInTimerThread(false);
135 mTimerImpl
= nullptr;
138 // Called with the Monitor held, but not the TimerImpl's mutex
139 already_AddRefed
<nsTimerImpl
> Take() {
140 if (MOZ_LIKELY(mTimerImpl
)) {
141 MOZ_ASSERT(mTimerImpl
->IsInTimerThread());
142 mTimerImpl
->SetIsInTimerThread(false);
144 return mTimerImpl
.forget();
147 const TimeStamp
& Timeout() const { return mTimeout
; }
148 const TimeDuration
& Delay() const { return mDelay
; }
151 // These values are simply cached from the timer. Keeping them here is good
152 // for cache usage and allows us to avoid worrying about locking conflicts
157 RefPtr
<nsTimerImpl
> mTimerImpl
;
160 // Computes and returns the index in mTimers at which a new timer with the
161 // specified timeout should be inserted in order to maintain "sorted" order.
162 size_t ComputeTimerInsertionIndex(const TimeStamp
& timeout
) const
163 MOZ_REQUIRES(mMonitor
);
165 // Computes and returns when we should next try to wake up in order to handle
166 // the triggering of the timers in mTimers. Currently this is very simple and
167 // we always just plan to wake up for the next timer in the list. In the
168 // future this will be more sophisticated.
169 TimeStamp
ComputeWakeupTimeFromTimers() const MOZ_REQUIRES(mMonitor
);
171 // Computes how late a timer can acceptably fire.
172 // timerDuration is the duration of the timer whose delay we are calculating.
173 // Longer timers can tolerate longer firing delays.
174 // minDelay is an amount by which any timer can be delayed.
175 // This function will never return a value smaller than minDelay (unless this
176 // conflicts with maxDelay). maxDelay is the upper limit on the amount by
177 // which we will ever delay any timer. Takes precedence over minDelay if there
178 // is a conflict. (Zero will effectively disable timer coalescing.)
179 TimeDuration
ComputeAcceptableFiringDelay(TimeDuration timerDuration
,
180 TimeDuration minDelay
,
181 TimeDuration maxDelay
) const;
184 UINT
ComputeDesiredTimerPeriod() const;
188 // Checks mTimers to see if any entries are out of order or any cached
189 // timeouts are incorrect and will assert if any inconsistency is found. Has
190 // no side effects other than asserting so has no use in non-DEBUG builds.
191 void VerifyTimerListConsistency() const MOZ_REQUIRES(mMonitor
);
194 // mTimers is maintained in a "pseudo-sorted" order wrt the timeouts.
195 // Specifcally, mTimers is sorted according to the timeouts *if you ignore the
196 // canceled entries* (those whose mTimerImpl is nullptr). Notably this means
197 // that you cannot use a binary search on this list.
198 nsTArray
<Entry
> mTimers
MOZ_GUARDED_BY(mMonitor
);
200 // Set only at the start of the thread's Run():
201 uint32_t mAllowedEarlyFiringMicroseconds
MOZ_GUARDED_BY(mMonitor
);
203 ProfilerThreadId mProfilerThreadId
MOZ_GUARDED_BY(mMonitor
);
205 // Time at which we were intending to wake up the last time that we slept.
206 // Is "null" if we have never slept or if our last sleep was "forever".
207 TimeStamp mIntendedWakeupTime
;
209 #if TIMER_THREAD_STATISTICS
210 static constexpr size_t sTimersFiredPerWakeupBucketCount
= 16;
211 static inline constexpr std::array
<size_t, sTimersFiredPerWakeupBucketCount
>
212 sTimersFiredPerWakeupThresholds
= {
213 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 20, 30, 40, 50, 70, (size_t)(-1)};
215 mutable AutoTArray
<size_t, sTimersFiredPerWakeupBucketCount
>
216 mTimersFiredPerWakeup
MOZ_GUARDED_BY(mMonitor
) = {0, 0, 0, 0, 0, 0, 0, 0,
217 0, 0, 0, 0, 0, 0, 0, 0};
218 mutable AutoTArray
<size_t, sTimersFiredPerWakeupBucketCount
>
219 mTimersFiredPerUnnotifiedWakeup
MOZ_GUARDED_BY(mMonitor
) = {
220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
221 mutable AutoTArray
<size_t, sTimersFiredPerWakeupBucketCount
>
222 mTimersFiredPerNotifiedWakeup
MOZ_GUARDED_BY(mMonitor
) = {
223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
225 mutable size_t mTotalTimersAdded
MOZ_GUARDED_BY(mMonitor
) = 0;
226 mutable size_t mTotalTimersRemoved
MOZ_GUARDED_BY(mMonitor
) = 0;
227 mutable size_t mTotalTimersFiredNotified
MOZ_GUARDED_BY(mMonitor
) = 0;
228 mutable size_t mTotalTimersFiredUnnotified
MOZ_GUARDED_BY(mMonitor
) = 0;
230 mutable size_t mTotalWakeupCount
MOZ_GUARDED_BY(mMonitor
) = 0;
231 mutable size_t mTotalUnnotifiedWakeupCount
MOZ_GUARDED_BY(mMonitor
) = 0;
232 mutable size_t mTotalNotifiedWakeupCount
MOZ_GUARDED_BY(mMonitor
) = 0;
234 mutable double mTotalActualTimerFiringDelayNotified
MOZ_GUARDED_BY(mMonitor
) =
236 mutable double mTotalActualTimerFiringDelayUnnotified
237 MOZ_GUARDED_BY(mMonitor
) = 0.0;
239 mutable TimeStamp mFirstTimerAdded
MOZ_GUARDED_BY(mMonitor
);
241 mutable size_t mEarlyWakeups
MOZ_GUARDED_BY(mMonitor
) = 0;
242 mutable double mTotalEarlyWakeupTime
MOZ_GUARDED_BY(mMonitor
) = 0.0;
244 void PrintStatistics() const;
247 #endif /* TimerThread_h___ */