Bug 1941046 - Part 4: Send a callback request for impression and clicks of MARS Top...
[gecko.git] / dom / media / MediaTimer.h
blobf9cc44cf13d5e0e5f0ac03a22db7c61a6e87f58e
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_)
8 # define MediaTimer_h_
10 # include <queue>
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"
22 namespace mozilla {
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
39 // interface.
40 template <typename T>
41 class MediaTimer {
42 public:
43 explicit MediaTimer(bool aFuzzy = false);
45 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTimer,
46 DispatchDestroy());
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.
55 void Cancel();
57 private:
58 virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
60 void DispatchDestroy();
61 // Runs on the timer thread.
62 void Destroy();
63 bool OnMediaTimerThread();
64 void ScheduleUpdate();
65 void Update();
66 void UpdateLocked();
67 bool IsExpired(const T& aTarget, const T& aNow);
68 void Reject();
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);
76 void TimerFired();
77 void ArmTimer(const T& aTarget, const T& aNow);
79 bool TimerIsArmed();
80 void CancelTimerIfArmed();
82 struct Entry {
83 T mTimeStamp;
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
105 // logging purposes.
106 T mCreationTimeStamp;
107 int64_t RelativeMicroseconds(const T& aTimeStamp) {
108 return (int64_t)(aTimeStamp - mCreationTimeStamp).ToMicroseconds();
111 bool mUpdateScheduled;
112 const bool mFuzzy;
115 // Class for managing delayed dispatches on target thread.
116 template <typename T>
117 class DelayedScheduler {
118 public:
119 explicit DelayedScheduler(nsISerialEventTarget* aTargetThread,
120 bool aFuzzy = false)
121 : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer<T>(aFuzzy)) {
122 MOZ_ASSERT(mTargetThread);
125 bool IsScheduled() const { return mTarget.isSome(); }
127 void Reset() {
128 MOZ_ASSERT(mTargetThread->IsOnCurrentThread(),
129 "Must be on target thread to disconnect");
130 mRequest.DisconnectIfExists();
131 mTarget = Nothing();
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) {
138 return;
140 Reset();
141 mTarget.emplace(aTarget);
142 mMediaTimer->WaitUntil(mTarget.value(), __func__)
143 ->Then(mTargetThread, __func__, std::forward<ResolveFunc>(aResolver),
144 std::forward<RejectFunc>(aRejector))
145 ->Track(mRequest);
148 void CompleteRequest() {
149 MOZ_ASSERT(mTargetThread->IsOnCurrentThread());
150 mRequest.Complete();
151 mTarget = Nothing();
154 private:
155 nsCOMPtr<nsISerialEventTarget> mTargetThread;
156 RefPtr<MediaTimer<T>> mMediaTimer;
157 Maybe<T> mTarget;
158 MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
161 using MediaTimerTimeStamp = MediaTimer<TimeStamp>;
162 using MediaTimerAwakeTimeStamp = MediaTimer<AwakeTimeStamp>;
164 } // namespace mozilla
166 #endif