Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / media / MediaTimer.h
blob837a1591b36700b9eaec416508d95aebe2f2cb88
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/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"
21 namespace mozilla {
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
38 // interface.
39 class MediaTimer {
40 public:
41 explicit MediaTimer(bool aFuzzy = false);
43 NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTimer,
44 DispatchDestroy());
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.
52 private:
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();
60 void Update();
61 void UpdateLocked();
62 bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow);
63 void Reject();
65 static void TimerCallback(nsITimer* aTimer, void* aClosure);
66 void TimerFired();
67 void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);
69 bool TimerIsArmed() { return !mCurrentTimerTarget.IsNull(); }
71 void CancelTimerIfArmed() {
72 MOZ_ASSERT(OnMediaTimerThread());
73 if (TimerIsArmed()) {
74 TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
75 mTimer->Cancel();
76 mCurrentTimerTarget = TimeStamp();
80 struct Entry {
81 TimeStamp mTimeStamp;
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
103 // logging purposes.
104 TimeStamp mCreationTimeStamp;
105 int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp) {
106 return (int64_t)(aTimeStamp - mCreationTimeStamp).ToMicroseconds();
109 bool mUpdateScheduled;
110 const bool mFuzzy;
113 // Class for managing delayed dispatches on target thread.
114 class DelayedScheduler {
115 public:
116 explicit DelayedScheduler(nsISerialEventTarget* aTargetThread,
117 bool aFuzzy = false)
118 : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer(aFuzzy)) {
119 MOZ_ASSERT(mTargetThread);
122 bool IsScheduled() const { return !mTarget.IsNull(); }
124 void Reset() {
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) {
136 return;
138 Reset();
139 mTarget = aTarget;
140 mMediaTimer->WaitUntil(mTarget, __func__)
141 ->Then(mTargetThread, __func__, std::forward<ResolveFunc>(aResolver),
142 std::forward<RejectFunc>(aRejector))
143 ->Track(mRequest);
146 void CompleteRequest() {
147 MOZ_ASSERT(mTargetThread->IsOnCurrentThread());
148 mRequest.Complete();
149 mTarget = TimeStamp();
152 private:
153 nsCOMPtr<nsISerialEventTarget> mTargetThread;
154 RefPtr<MediaTimer> mMediaTimer;
155 MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
156 TimeStamp mTarget;
159 } // namespace mozilla
161 #endif