MediaTimer.h 4.7 KB

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