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/IntegerPrintfMacros.h"
14 # include "mozilla/Monitor.h"
15 # include "mozilla/MozPromise.h"
16 # include "mozilla/RefPtr.h"
17 # include "mozilla/TimeStamp.h"
18 # include "mozilla/Unused.h"
19 # include "nsITimer.h"
23 extern LazyLogModule gMediaTimerLog
;
25 # define TIMER_LOG(x, ...) \
26 MOZ_ASSERT(gMediaTimerLog); \
27 MOZ_LOG(gMediaTimerLog, LogLevel::Debug, \
28 ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \
29 RelativeMicroseconds(TimeStamp::Now()), ##__VA_ARGS__))
31 // This promise type is only exclusive because so far there isn't a reason for
32 // it not to be. Feel free to change that.
33 typedef MozPromise
<bool, bool, /* IsExclusive = */ true> MediaTimerPromise
;
35 // Timers only know how to fire at a given thread, which creates an impedence
36 // mismatch with code that operates with TaskQueues. This class solves
37 // that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
41 explicit MediaTimer(bool aFuzzy
= false);
43 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTimer
,
46 RefPtr
<MediaTimerPromise
> WaitFor(const TimeDuration
& aDuration
,
47 const char* aCallSite
);
48 RefPtr
<MediaTimerPromise
> WaitUntil(const TimeStamp
& aTimeStamp
,
49 const char* aCallSite
);
50 void Cancel(); // Cancel and reject any unresolved promises with false.
53 virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
55 void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
56 void Destroy(); // Runs on the timer thread.
58 bool OnMediaTimerThread();
59 void ScheduleUpdate();
62 bool IsExpired(const TimeStamp
& aTarget
, const TimeStamp
& aNow
);
65 static void TimerCallback(nsITimer
* aTimer
, void* aClosure
);
67 void ArmTimer(const TimeStamp
& aTarget
, const TimeStamp
& aNow
);
69 bool TimerIsArmed() { return !mCurrentTimerTarget
.IsNull(); }
71 void CancelTimerIfArmed() {
72 MOZ_ASSERT(OnMediaTimerThread());
74 TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
76 mCurrentTimerTarget
= TimeStamp();
82 RefPtr
<MediaTimerPromise::Private
> mPromise
;
84 explicit Entry(const TimeStamp
& aTimeStamp
, const char* aCallSite
)
85 : mTimeStamp(aTimeStamp
),
86 mPromise(new MediaTimerPromise::Private(aCallSite
)) {}
88 // Define a < overload that reverses ordering because std::priority_queue
89 // provides access to the largest element, and we want the smallest
90 // (i.e. the soonest).
91 bool operator<(const Entry
& aOther
) const {
92 return mTimeStamp
> aOther
.mTimeStamp
;
96 nsCOMPtr
<nsIEventTarget
> mThread
;
97 std::priority_queue
<Entry
> mEntries
;
98 Monitor mMonitor MOZ_UNANNOTATED
;
99 nsCOMPtr
<nsITimer
> mTimer
;
100 TimeStamp mCurrentTimerTarget
;
102 // Timestamps only have relative meaning, so we need a base timestamp for
104 TimeStamp mCreationTimeStamp
;
105 int64_t RelativeMicroseconds(const TimeStamp
& aTimeStamp
) {
106 return (int64_t)(aTimeStamp
- mCreationTimeStamp
).ToMicroseconds();
109 bool mUpdateScheduled
;
113 // Class for managing delayed dispatches on target thread.
114 class DelayedScheduler
{
116 explicit DelayedScheduler(nsISerialEventTarget
* aTargetThread
,
118 : mTargetThread(aTargetThread
), mMediaTimer(new MediaTimer(aFuzzy
)) {
119 MOZ_ASSERT(mTargetThread
);
122 bool IsScheduled() const { return !mTarget
.IsNull(); }
125 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread(),
126 "Must be on target thread to disconnect");
127 mRequest
.DisconnectIfExists();
128 mTarget
= TimeStamp();
131 template <typename ResolveFunc
, typename RejectFunc
>
132 void Ensure(mozilla::TimeStamp
& aTarget
, ResolveFunc
&& aResolver
,
133 RejectFunc
&& aRejector
) {
134 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread());
135 if (IsScheduled() && mTarget
<= aTarget
) {
140 mMediaTimer
->WaitUntil(mTarget
, __func__
)
141 ->Then(mTargetThread
, __func__
, std::forward
<ResolveFunc
>(aResolver
),
142 std::forward
<RejectFunc
>(aRejector
))
146 void CompleteRequest() {
147 MOZ_ASSERT(mTargetThread
->IsOnCurrentThread());
149 mTarget
= TimeStamp();
153 nsCOMPtr
<nsISerialEventTarget
> mTargetThread
;
154 RefPtr
<MediaTimer
> mMediaTimer
;
155 MozPromiseRequestHolder
<mozilla::MediaTimerPromise
> mRequest
;
159 } // namespace mozilla