/work/obj-fuzz/dist/include/MediaTimer.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #if !defined(MediaTimer_h_) |
8 | | #define MediaTimer_h_ |
9 | | |
10 | | #include "mozilla/AbstractThread.h" |
11 | | #include "mozilla/IntegerPrintfMacros.h" |
12 | | #include "mozilla/Monitor.h" |
13 | | #include "mozilla/MozPromise.h" |
14 | | #include "mozilla/RefPtr.h" |
15 | | #include "mozilla/TimeStamp.h" |
16 | | #include "mozilla/Unused.h" |
17 | | #include "nsITimer.h" |
18 | | #include <queue> |
19 | | |
20 | | namespace mozilla { |
21 | | |
22 | | extern LazyLogModule gMediaTimerLog; |
23 | | |
24 | | #define TIMER_LOG(x, ...) \ |
25 | | MOZ_ASSERT(gMediaTimerLog); \ |
26 | | MOZ_LOG(gMediaTimerLog, LogLevel::Debug, ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \ |
27 | | RelativeMicroseconds(TimeStamp::Now()), ##__VA_ARGS__)) |
28 | | |
29 | | // This promise type is only exclusive because so far there isn't a reason for |
30 | | // it not to be. Feel free to change that. |
31 | | typedef MozPromise<bool, bool, /* IsExclusive = */ true> MediaTimerPromise; |
32 | | |
33 | | // Timers only know how to fire at a given thread, which creates an impedence |
34 | | // mismatch with code that operates with TaskQueues. This class solves |
35 | | // that mismatch with a dedicated (but shared) thread and a nice MozPromise-y |
36 | | // interface. |
37 | | class MediaTimer |
38 | | { |
39 | | public: |
40 | | explicit MediaTimer(bool aFuzzy = false); |
41 | | |
42 | | // We use a release with a custom Destroy(). |
43 | | NS_IMETHOD_(MozExternalRefCountType) AddRef(void); |
44 | | NS_IMETHOD_(MozExternalRefCountType) Release(void); |
45 | | |
46 | | RefPtr<MediaTimerPromise> WaitFor(const TimeDuration& aDuration, const char* aCallSite); |
47 | | RefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite); |
48 | | void Cancel(); // Cancel and reject any unresolved promises with false. |
49 | | |
50 | | private: |
51 | | virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); } |
52 | | |
53 | | void DispatchDestroy(); // Invoked by Release on an arbitrary thread. |
54 | | void Destroy(); // Runs on the timer thread. |
55 | | |
56 | | bool OnMediaTimerThread(); |
57 | | void ScheduleUpdate(); |
58 | | void Update(); |
59 | | void UpdateLocked(); |
60 | | bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow); |
61 | | void Reject(); |
62 | | |
63 | | static void TimerCallback(nsITimer* aTimer, void* aClosure); |
64 | | void TimerFired(); |
65 | | void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow); |
66 | | |
67 | | bool TimerIsArmed() |
68 | | { |
69 | | return !mCurrentTimerTarget.IsNull(); |
70 | | } |
71 | | |
72 | | void CancelTimerIfArmed() |
73 | | { |
74 | | MOZ_ASSERT(OnMediaTimerThread()); |
75 | | if (TimerIsArmed()) { |
76 | | TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer"); |
77 | | mTimer->Cancel(); |
78 | | mCurrentTimerTarget = TimeStamp(); |
79 | | } |
80 | | } |
81 | | |
82 | | |
83 | | struct Entry |
84 | | { |
85 | | TimeStamp mTimeStamp; |
86 | | RefPtr<MediaTimerPromise::Private> mPromise; |
87 | | |
88 | | explicit Entry(const TimeStamp& aTimeStamp, const char* aCallSite) |
89 | | : mTimeStamp(aTimeStamp) |
90 | | , mPromise(new MediaTimerPromise::Private(aCallSite)) |
91 | | {} |
92 | | |
93 | | // Define a < overload that reverses ordering because std::priority_queue |
94 | | // provides access to the largest element, and we want the smallest |
95 | | // (i.e. the soonest). |
96 | | bool operator<(const Entry& aOther) const |
97 | | { |
98 | | return mTimeStamp > aOther.mTimeStamp; |
99 | | } |
100 | | }; |
101 | | |
102 | | ThreadSafeAutoRefCnt mRefCnt; |
103 | | NS_DECL_OWNINGTHREAD |
104 | | nsCOMPtr<nsIEventTarget> mThread; |
105 | | std::priority_queue<Entry> mEntries; |
106 | | Monitor mMonitor; |
107 | | nsCOMPtr<nsITimer> mTimer; |
108 | | TimeStamp mCurrentTimerTarget; |
109 | | |
110 | | // Timestamps only have relative meaning, so we need a base timestamp for |
111 | | // logging purposes. |
112 | | TimeStamp mCreationTimeStamp; |
113 | | int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp) |
114 | | { |
115 | | return (int64_t) (aTimeStamp - mCreationTimeStamp).ToMicroseconds(); |
116 | | } |
117 | | |
118 | | bool mUpdateScheduled; |
119 | | const bool mFuzzy; |
120 | | }; |
121 | | |
122 | | // Class for managing delayed dispatches on target thread. |
123 | | class DelayedScheduler { |
124 | | public: |
125 | | explicit DelayedScheduler(AbstractThread* aTargetThread, bool aFuzzy = false) |
126 | | : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer(aFuzzy)) |
127 | | { |
128 | | MOZ_ASSERT(mTargetThread); |
129 | | } |
130 | | |
131 | | bool IsScheduled() const { return !mTarget.IsNull(); } |
132 | | |
133 | | void Reset() |
134 | | { |
135 | | MOZ_ASSERT(mTargetThread->IsCurrentThreadIn(), |
136 | | "Must be on target thread to disconnect"); |
137 | | if (IsScheduled()) { |
138 | | mRequest.Disconnect(); |
139 | | mTarget = TimeStamp(); |
140 | | } |
141 | | } |
142 | | |
143 | | template <typename ResolveFunc, typename RejectFunc> |
144 | | void Ensure(mozilla::TimeStamp& aTarget, |
145 | | ResolveFunc&& aResolver, |
146 | | RejectFunc&& aRejector) |
147 | 0 | { |
148 | 0 | MOZ_ASSERT(mTargetThread->IsCurrentThreadIn()); |
149 | 0 | if (IsScheduled() && mTarget <= aTarget) { |
150 | 0 | return; |
151 | 0 | } |
152 | 0 | Reset(); |
153 | 0 | mTarget = aTarget; |
154 | 0 | mMediaTimer->WaitUntil(mTarget, __func__)->Then( |
155 | 0 | mTargetThread, __func__, |
156 | 0 | std::forward<ResolveFunc>(aResolver), |
157 | 0 | std::forward<RejectFunc>(aRejector)) |
158 | 0 | ->Track(mRequest); |
159 | 0 | } Unexecuted instantiation: Unified_cpp_dom_media_mediasink0.cpp:void mozilla::DelayedScheduler::Ensure<mozilla::media::VideoSink::TryUpdateRenderedVideoFrames()::$_3, mozilla::media::VideoSink::TryUpdateRenderedVideoFrames()::$_4>(mozilla::TimeStamp&, mozilla::media::VideoSink::TryUpdateRenderedVideoFrames()::$_3&&, mozilla::media::VideoSink::TryUpdateRenderedVideoFrames()::$_4&&) Unexecuted instantiation: Unified_cpp_dom_media_mediasink0.cpp:void mozilla::DelayedScheduler::Ensure<mozilla::media::VideoSink::UpdateRenderedVideoFrames()::$_5, mozilla::media::VideoSink::UpdateRenderedVideoFrames()::$_6>(mozilla::TimeStamp&, mozilla::media::VideoSink::UpdateRenderedVideoFrames()::$_5&&, mozilla::media::VideoSink::UpdateRenderedVideoFrames()::$_6&&) Unexecuted instantiation: void mozilla::DelayedScheduler::Ensure<mozilla::DecryptThroughputLimit::Throttle(mozilla::MediaRawData*)::{lambda()#1}, mozilla::DecryptThroughputLimit::Throttle(mozilla::MediaRawData*)::{lambda()#2}>(mozilla::TimeStamp&, mozilla::DecryptThroughputLimit::Throttle(mozilla::MediaRawData*)::{lambda()#1}&&, mozilla::DecryptThroughputLimit::Throttle(mozilla::MediaRawData*)::{lambda()#2}&&) |
160 | | |
161 | | void CompleteRequest() |
162 | | { |
163 | | MOZ_ASSERT(mTargetThread->IsCurrentThreadIn()); |
164 | | mRequest.Complete(); |
165 | | mTarget = TimeStamp(); |
166 | | } |
167 | | |
168 | | private: |
169 | | RefPtr<AbstractThread> mTargetThread; |
170 | | RefPtr<MediaTimer> mMediaTimer; |
171 | | MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest; |
172 | | TimeStamp mTarget; |
173 | | }; |
174 | | |
175 | | } // namespace mozilla |
176 | | |
177 | | #endif |