Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / media / MediaTimer.cpp
blob6a69bc3de787cdbced651f99426fb9cab9dfb7dc
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/. */
7 #include "MediaTimer.h"
9 #include "mozilla/AwakeTimeStamp.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/SharedThreadPool.h"
13 #include "mozilla/Unused.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsThreadUtils.h"
16 #include <math.h>
18 namespace mozilla {
20 template <typename T>
21 MediaTimer<T>::MediaTimer(bool aFuzzy)
22 : mMonitor("MediaTimer Monitor"),
23 mCreationTimeStamp(T::Now()),
24 mUpdateScheduled(false),
25 mFuzzy(aFuzzy) {
26 TIMER_LOG("MediaTimer::MediaTimer");
28 // Use the SharedThreadPool to create an nsIThreadPool with a maximum of one
29 // thread, which is equivalent to an nsIThread for our purposes.
30 RefPtr<SharedThreadPool> threadPool(
31 SharedThreadPool::Get("MediaTimer"_ns, 1));
32 mThread = threadPool.get();
33 mTimer = NS_NewTimer(mThread);
36 template <typename T>
37 void MediaTimer<T>::DispatchDestroy() {
38 // Hold a strong reference to the thread so that it doesn't get deleted in
39 // Destroy(), which may run completely before the stack if Dispatch() begins
40 // to unwind.
41 nsCOMPtr<nsIEventTarget> thread = mThread;
42 nsresult rv =
43 thread->Dispatch(NewNonOwningRunnableMethod("MediaTimer::Destroy", this,
44 &MediaTimer::Destroy),
45 NS_DISPATCH_NORMAL);
46 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
47 Unused << rv;
48 (void)rv;
51 // Runs on the timer thread.
52 template <typename T>
53 void MediaTimer<T>::Destroy() {
54 MOZ_ASSERT(OnMediaTimerThread());
55 TIMER_LOG("MediaTimer::Destroy");
57 // Reject any outstanding entries.
59 MonitorAutoLock lock(mMonitor);
60 Reject();
62 // Cancel the timer if necessary.
63 CancelTimerIfArmed();
66 delete this;
69 template <typename T>
70 bool MediaTimer<T>::OnMediaTimerThread() {
71 bool rv = false;
72 mThread->IsOnCurrentThread(&rv);
73 return rv;
76 template <typename T>
77 RefPtr<MediaTimerPromise> MediaTimer<T>::WaitFor(
78 const typename T::DurationType& aDuration, StaticString aCallSite) {
79 return WaitUntil(T::Now() + aDuration, aCallSite);
82 template <typename T>
83 RefPtr<MediaTimerPromise> MediaTimer<T>::WaitUntil(const T& aTimeStamp,
84 StaticString aCallSite) {
85 MonitorAutoLock mon(mMonitor);
86 TIMER_LOG("MediaTimer::WaitUntil %" PRId64, RelativeMicroseconds(aTimeStamp));
87 Entry e(aTimeStamp, aCallSite);
88 RefPtr<MediaTimerPromise> p = e.mPromise.get();
89 mEntries.push(e);
90 ScheduleUpdate();
91 return p;
94 template <typename T>
95 void MediaTimer<T>::Cancel() {
96 MonitorAutoLock mon(mMonitor);
97 TIMER_LOG("MediaTimer::Cancel");
98 Reject();
101 template <typename T>
102 void MediaTimer<T>::ScheduleUpdate() {
103 mMonitor.AssertCurrentThreadOwns();
104 if (mUpdateScheduled) {
105 return;
107 mUpdateScheduled = true;
109 nsresult rv = mThread->Dispatch(
110 NewRunnableMethod("MediaTimer::Update", this, &MediaTimer::Update),
111 NS_DISPATCH_NORMAL);
112 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
113 Unused << rv;
114 (void)rv;
117 template <typename T>
118 void MediaTimer<T>::Update() {
119 MonitorAutoLock mon(mMonitor);
120 UpdateLocked();
123 template <typename T>
124 bool MediaTimer<T>::IsExpired(const T& aTarget, const T& aNow) {
125 MOZ_ASSERT(OnMediaTimerThread());
126 mMonitor.AssertCurrentThreadOwns();
127 // Treat this timer as expired in fuzzy mode even if it is fired
128 // slightly (< 1ms) before the schedule target. So we don't need to schedule
129 // a timer with very small timeout again when the client doesn't need a
130 // high-res timer.
131 T t = mFuzzy ? aTarget - T::DurationType::FromMilliseconds(1) : aTarget;
132 return t <= aNow;
135 template <typename T>
136 void MediaTimer<T>::UpdateLocked() {
137 MOZ_ASSERT(OnMediaTimerThread());
138 mMonitor.AssertCurrentThreadOwns();
139 mUpdateScheduled = false;
141 TIMER_LOG("MediaTimer::UpdateLocked");
143 // Resolve all the promises whose time is up.
144 T now = T::Now();
145 while (!mEntries.empty() && IsExpired(mEntries.top().mTimeStamp, now)) {
146 mEntries.top().mPromise->Resolve(true, __func__);
147 DebugOnly<T> poppedTimeStamp = mEntries.top().mTimeStamp;
148 mEntries.pop();
149 MOZ_ASSERT_IF(!mEntries.empty(),
150 *&poppedTimeStamp <= mEntries.top().mTimeStamp);
153 // If we've got no more entries, cancel any pending timer and bail out.
154 if (mEntries.empty()) {
155 CancelTimerIfArmed();
156 return;
159 // We've got more entries - (re)arm the timer for the soonest one.
160 if (!TimerIsArmed() ||
161 mEntries.top().mTimeStamp < mCurrentTimerTarget.value()) {
162 CancelTimerIfArmed();
163 ArmTimer(mEntries.top().mTimeStamp, now);
167 template <typename T>
168 void MediaTimer<T>::Reject() {
169 mMonitor.AssertCurrentThreadOwns();
170 while (!mEntries.empty()) {
171 mEntries.top().mPromise->Reject(false, __func__);
172 mEntries.pop();
176 template <typename T>
177 /* static */ void MediaTimer<T>::TimerCallback(nsITimer* aTimer,
178 void* aClosure) {
179 static_cast<MediaTimer<T>*>(aClosure)->TimerFired();
182 template <typename T>
183 void MediaTimer<T>::TimerFired() {
184 MonitorAutoLock mon(mMonitor);
185 MOZ_ASSERT(OnMediaTimerThread());
186 mCurrentTimerTarget = Nothing();
187 UpdateLocked();
190 template <typename T>
191 void MediaTimer<T>::ArmTimer(const T& aTarget, const T& aNow) {
192 MOZ_DIAGNOSTIC_ASSERT(!TimerIsArmed());
193 MOZ_DIAGNOSTIC_ASSERT(aTarget > aNow);
195 const typename T::DurationType delay = aTarget - aNow;
196 TIMER_LOG("MediaTimer::ArmTimer delay=%.3fms", delay.ToMilliseconds());
197 mCurrentTimerTarget.emplace(aTarget);
198 TimeDuration duration =
199 TimeDuration::FromMicroseconds(delay.ToMicroseconds());
200 MOZ_ALWAYS_SUCCEEDS(mTimer->InitHighResolutionWithNamedFuncCallback(
201 &TimerCallback, this, duration, nsITimer::TYPE_ONE_SHOT,
202 "MediaTimer::TimerCallback"));
205 template <typename T>
206 bool MediaTimer<T>::TimerIsArmed() {
207 return mCurrentTimerTarget.isSome();
210 template <typename T>
211 void MediaTimer<T>::CancelTimerIfArmed() {
212 MOZ_ASSERT(OnMediaTimerThread());
213 if (TimerIsArmed()) {
214 TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
215 mTimer->Cancel();
216 mCurrentTimerTarget = Nothing();
220 template class MediaTimer<AwakeTimeStamp>;
221 template class MediaTimer<TimeStamp>;
223 } // namespace mozilla