Bug 1430948. P1 - add fuzzy mode to MediaTimer. draft
authorJW Wang <jwwang@mozilla.com>
Tue, 16 Jan 2018 16:07:21 +0800
changeset 721915 91dfe27787c268087b7d9302228d483f6c9437f8
parent 721830 b7a651281314d6369658eeb58e3bb181cf95016f
child 721916 12552221c0180adbc7b44f2ae1df916313b09ced
push id95997
push userjwwang@mozilla.com
push dateThu, 18 Jan 2018 02:54:00 +0000
bugs1430948
milestone59.0a1
Bug 1430948. P1 - add fuzzy mode to MediaTimer. We will resolve the timer promise even if it is fired slightly before the scheduled target. This is used by MDSM which doesn't require high-res timers. MozReview-Commit-ID: 1IAsG5gG9D7
dom/media/MediaTimer.cpp
dom/media/MediaTimer.h
--- a/dom/media/MediaTimer.cpp
+++ b/dom/media/MediaTimer.cpp
@@ -13,21 +13,22 @@
 #include "nsThreadUtils.h"
 #include <math.h>
 
 namespace mozilla {
 
 NS_IMPL_ADDREF(MediaTimer)
 NS_IMPL_RELEASE_WITH_DESTROY(MediaTimer, DispatchDestroy())
 
-MediaTimer::MediaTimer()
+MediaTimer::MediaTimer(bool aFuzzy)
   : mMonitor("MediaTimer Monitor")
   , mTimer(NS_NewTimer())
   , mCreationTimeStamp(TimeStamp::Now())
   , mUpdateScheduled(false)
+  , mFuzzy(aFuzzy)
 {
   TIMER_LOG("MediaTimer::MediaTimer");
 
   // Use the SharedThreadPool to create an nsIThreadPool with a maximum of one
   // thread, which is equivalent to an nsIThread for our purposes.
   RefPtr<SharedThreadPool> threadPool(
     SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaTimer"), 1));
   mThread = threadPool.get();
@@ -109,28 +110,41 @@ MediaTimer::ScheduleUpdate()
 
 void
 MediaTimer::Update()
 {
   MonitorAutoLock mon(mMonitor);
   UpdateLocked();
 }
 
+bool
+MediaTimer::IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow)
+{
+  MOZ_ASSERT(OnMediaTimerThread());
+  mMonitor.AssertCurrentThreadOwns();
+  // Treat this timer as expired in fuzzy mode even if it is fired
+  // slightly (< 1ms) before the schedule target. So we don't need to schedule a
+  // timer with very small timeout again when the client doesn't need a high-res
+  // timer.
+  TimeStamp t = mFuzzy ? aTarget - TimeDuration::FromMilliseconds(1) : aTarget;
+  return t <= aNow;
+}
+
 void
 MediaTimer::UpdateLocked()
 {
   MOZ_ASSERT(OnMediaTimerThread());
   mMonitor.AssertCurrentThreadOwns();
   mUpdateScheduled = false;
 
   TIMER_LOG("MediaTimer::UpdateLocked");
 
   // Resolve all the promises whose time is up.
   TimeStamp now = TimeStamp::Now();
-  while (!mEntries.empty() && mEntries.top().mTimeStamp <= now) {
+  while (!mEntries.empty() && IsExpired(mEntries.top().mTimeStamp, now)) {
     mEntries.top().mPromise->Resolve(true, __func__);
     DebugOnly<TimeStamp> poppedTimeStamp = mEntries.top().mTimeStamp;
     mEntries.pop();
     MOZ_ASSERT_IF(!mEntries.empty(), *&poppedTimeStamp <= mEntries.top().mTimeStamp);
   }
 
   // If we've got no more entries, cancel any pending timer and bail out.
   if (mEntries.empty()) {
--- a/dom/media/MediaTimer.h
+++ b/dom/media/MediaTimer.h
@@ -31,17 +31,17 @@ typedef MozPromise<bool, bool, /* IsExcl
 
 // 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();
+  explicit MediaTimer(bool aFuzzy = false);
 
   // 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:
@@ -49,16 +49,17 @@ private:
 
   void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
   void Destroy(); // Runs on the timer thread.
 
   bool OnMediaTimerThread();
   void ScheduleUpdate();
   void Update();
   void UpdateLocked();
+  bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow);
 
   static void TimerCallback(nsITimer* aTimer, void* aClosure);
   void TimerFired();
   void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);
 
   bool TimerIsArmed()
   {
     return !mCurrentTimerTarget.IsNull();
@@ -106,23 +107,24 @@ private:
   // logging purposes.
   TimeStamp mCreationTimeStamp;
   int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp)
   {
     return (int64_t) (aTimeStamp - mCreationTimeStamp).ToMicroseconds();
   }
 
   bool mUpdateScheduled;
+  const bool mFuzzy;
 };
 
 // Class for managing delayed dispatches on target thread.
 class DelayedScheduler {
 public:
-  explicit DelayedScheduler(AbstractThread* aTargetThread)
-    : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer())
+  explicit DelayedScheduler(AbstractThread* aTargetThread, bool aFuzzy = false)
+    : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer(aFuzzy))
   {
     MOZ_ASSERT(mTargetThread);
   }
 
   bool IsScheduled() const { return !mTarget.IsNull(); }
 
   void Reset()
   {