123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cindent: */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #if !defined(MediaTimer_h_)
- #define MediaTimer_h_
- #include "mozilla/Monitor.h"
- #include "mozilla/MozPromise.h"
- #include "mozilla/TimeStamp.h"
- #include <queue>
- #include "nsITimer.h"
- #include "mozilla/RefPtr.h"
- namespace mozilla {
- extern LazyLogModule gMediaTimerLog;
- #define TIMER_LOG(x, ...) \
- MOZ_ASSERT(gMediaTimerLog); \
- MOZ_LOG(gMediaTimerLog, LogLevel::Debug, ("[MediaTimer=%p relative_t=%lld]" x, this, \
- RelativeMicroseconds(TimeStamp::Now()), ##__VA_ARGS__))
- // This promise type is only exclusive because so far there isn't a reason for
- // it not to be. Feel free to change that.
- typedef MozPromise<bool, bool, /* IsExclusive = */ true> MediaTimerPromise;
- // Timers only know how to fire at a given thread, which creates an impedence
- // mismatch with code that operates with TaskQueues. This class solves
- // that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
- // interface.
- class MediaTimer
- {
- public:
- MediaTimer();
- // We use a release with a custom Destroy().
- NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
- NS_IMETHOD_(MozExternalRefCountType) Release(void);
- RefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite);
- private:
- virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }
- void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
- void Destroy(); // Runs on the timer thread.
- bool OnMediaTimerThread();
- void ScheduleUpdate();
- void Update();
- void UpdateLocked();
- static void TimerCallback(nsITimer* aTimer, void* aClosure);
- void TimerFired();
- void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);
- bool TimerIsArmed()
- {
- return !mCurrentTimerTarget.IsNull();
- }
- void CancelTimerIfArmed()
- {
- MOZ_ASSERT(OnMediaTimerThread());
- if (TimerIsArmed()) {
- TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
- mTimer->Cancel();
- mCurrentTimerTarget = TimeStamp();
- }
- }
- struct Entry
- {
- TimeStamp mTimeStamp;
- RefPtr<MediaTimerPromise::Private> mPromise;
- explicit Entry(const TimeStamp& aTimeStamp, const char* aCallSite)
- : mTimeStamp(aTimeStamp)
- , mPromise(new MediaTimerPromise::Private(aCallSite))
- {}
- // Define a < overload that reverses ordering because std::priority_queue
- // provides access to the largest element, and we want the smallest
- // (i.e. the soonest).
- bool operator<(const Entry& aOther) const
- {
- return mTimeStamp > aOther.mTimeStamp;
- }
- };
- ThreadSafeAutoRefCnt mRefCnt;
- NS_DECL_OWNINGTHREAD
- nsCOMPtr<nsIEventTarget> mThread;
- std::priority_queue<Entry> mEntries;
- Monitor mMonitor;
- nsCOMPtr<nsITimer> mTimer;
- TimeStamp mCurrentTimerTarget;
- // Timestamps only have relative meaning, so we need a base timestamp for
- // logging purposes.
- TimeStamp mCreationTimeStamp;
- int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp)
- {
- return (int64_t) (aTimeStamp - mCreationTimeStamp).ToMicroseconds();
- }
- bool mUpdateScheduled;
- };
- // Class for managing delayed dispatches on target thread.
- class DelayedScheduler {
- public:
- explicit DelayedScheduler(AbstractThread* aTargetThread)
- : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer())
- {
- MOZ_ASSERT(mTargetThread);
- }
- bool IsScheduled() const { return !mTarget.IsNull(); }
- void Reset()
- {
- MOZ_ASSERT(mTargetThread->IsCurrentThreadIn(),
- "Must be on target thread to disconnect");
- if (IsScheduled()) {
- mRequest.Disconnect();
- mTarget = TimeStamp();
- }
- }
- template <typename ResolveFunc, typename RejectFunc>
- void Ensure(mozilla::TimeStamp& aTarget,
- ResolveFunc&& aResolver,
- RejectFunc&& aRejector)
- {
- MOZ_ASSERT(mTargetThread->IsCurrentThreadIn());
- if (IsScheduled() && mTarget <= aTarget) {
- return;
- }
- Reset();
- mTarget = aTarget;
- mRequest.Begin(mMediaTimer->WaitUntil(mTarget, __func__)->Then(
- mTargetThread, __func__,
- Forward<ResolveFunc>(aResolver),
- Forward<RejectFunc>(aRejector)));
- }
- void CompleteRequest()
- {
- MOZ_ASSERT(mTargetThread->IsCurrentThreadIn());
- mRequest.Complete();
- mTarget = TimeStamp();
- }
- private:
- RefPtr<AbstractThread> mTargetThread;
- RefPtr<MediaTimer> mMediaTimer;
- MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
- TimeStamp mTarget;
- };
- } // namespace mozilla
- #endif
|