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 #if !defined(MediaTimer_h_)
12 # include "mozilla/AbstractThread.h"
13 # include "mozilla/AwakeTimeStamp.h"
14 # include "mozilla/Monitor.h"
15 # include "mozilla/MozPromise.h"
16 # include "mozilla/RefPtr.h"
17 # include "mozilla/SharedThreadPool.h"
18 # include "mozilla/TimeStamp.h"
19 # include "mozilla/Unused.h"
20 # include "nsITimer.h"
24 extern LazyLogModule gMediaTimerLog
;
26 # define TIMER_LOG(x, ...) \
27 MOZ_ASSERT(gMediaTimerLog); \
28 MOZ_LOG(gMediaTimerLog, LogLevel::Debug, \
29 ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \
30 RelativeMicroseconds(T::Now()), ##__VA_ARGS__))
32 // This promise type is only exclusive because so far there isn't a reason for
33 // it not to be. Feel free to change that.
34 using MediaTimerPromise
= MozPromise
<bool, bool, true>;
36 // Timers only know how to fire at a given thread, which creates an impedence
37 // mismatch with code that operates with TaskQueues. This class solves
38 // that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
43 explicit MediaTimer(bool aFuzzy
= false);
45 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTimer
,
48 RefPtr
<MediaTimerPromise
> WaitFor(const typename
T::DurationType
& aDuration
,
49 StaticString aCallSite
);
51 RefPtr
<MediaTimerPromise
> WaitUntil(const T
& aTimeStamp
,
52 StaticString aCallSite
);
54 // Cancel and reject any unresolved promises with false.
58 virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
60 void DispatchDestroy();
61 // Runs on the timer thread.
63 bool OnMediaTimerThread();
64 void ScheduleUpdate();
67 bool IsExpired(const T
& aTarget
, const T
& aNow
);
70 * We use a callback function, rather than a callback method, to ensure that
71 * the nsITimer does not artifically keep the refcount of the MediaTimer above
72 * zero. When the MediaTimer is destroyed, it safely cancels the nsITimer so
73 * that we never fire against a dangling closure.
75 static void TimerCallback(nsITimer
* aTimer
, void* aClosure
);
77 void ArmTimer(const T
& aTarget
, const T
& aNow
);
80 void CancelTimerIfArmed();
84 RefPtr
<MediaTimerPromise::Private
> mPromise
;
86 explicit Entry(const T
& aTimeStamp
, StaticString aCallSite
)
87 : mTimeStamp(aTimeStamp
),
88 mPromise(new MediaTimerPromise::Private(aCallSite
)) {}
90 // Define a < overload that reverses ordering because std::priority_queue
91 // provides access to the largest element, and we want the smallest
92 // (i.e. the soonest).
93 bool operator<(const Entry
& aOther
) const {
94 return mTimeStamp
> aOther
.mTimeStamp
;
98 nsCOMPtr
<nsIEventTarget
> mThread
;
99 std::priority_queue
<Entry
> mEntries
;
100 Monitor mMonitor MOZ_UNANNOTATED
;
101 nsCOMPtr
<nsITimer
> mTimer
;
102 Maybe
<T
> mCurrentTimerTarget
;
104 // Timestamps only have relative meaning, so we need a base timestamp for
106 T mCreationTimeStamp
;
107 int64_t RelativeMicroseconds(const T
& aTimeStamp
) {
108 return (int64_t)(aTimeStamp
- mCreationTimeStamp
).ToMicroseconds();
111 bool mUpdateScheduled
;
115 // Class for managing delayed dispatches on target thread.
116 template <typename T
>
117 class DelayedScheduler
{
119 explicit DelayedScheduler(nsISerialEventTarget
* aTargetThread
,
121 : mTargetThread(aTargetThread
), mMediaTimer(new MediaTimer
<T
>(aFuzzy
)) {
122 MOZ_ASSERT(mTargetThread
);
125 bool IsScheduled() const { return mTarget
.isSome(); }
128 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread(),
129 "Must be on target thread to disconnect");
130 mRequest
.DisconnectIfExists();
134 template <typename ResolveFunc
, typename RejectFunc
>
135 void Ensure(T
& aTarget
, ResolveFunc
&& aResolver
, RejectFunc
&& aRejector
) {
136 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread());
137 if (IsScheduled() && mTarget
.value() <= aTarget
) {
141 mTarget
.emplace(aTarget
);
142 mMediaTimer
->WaitUntil(mTarget
.value(), __func__
)
143 ->Then(mTargetThread
, __func__
, std::forward
<ResolveFunc
>(aResolver
),
144 std::forward
<RejectFunc
>(aRejector
))
148 void CompleteRequest() {
149 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread());
155 nsCOMPtr
<nsISerialEventTarget
> mTargetThread
;
156 RefPtr
<MediaTimer
<T
>> mMediaTimer
;
158 MozPromiseRequestHolder
<mozilla::MediaTimerPromise
> mRequest
;
161 using MediaTimerTimeStamp
= MediaTimer
<TimeStamp
>;
162 using MediaTimerAwakeTimeStamp
= MediaTimer
<AwakeTimeStamp
>;
164 } // namespace mozilla