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