Bug 1299515 - Implement MediaTimer::Cancel to allow for rejecting timer promises. r?jya draft
authorAndreas Pehrson <pehrsons@mozilla.com>
Wed, 20 Dec 2017 10:02:31 +0100
changeset 749424 3a1aa4108affecb32a57cdab76fe843d94309cc7
parent 749423 41a9d97ae0f9ddb0aec9d7474cdfa11b93beb1c5
child 749425 c308ecbfb205a7e83005e273472f7b3fbb03dc04
push id97396
push userbmo:apehrson@mozilla.com
push dateWed, 31 Jan 2018 13:27:39 +0000
reviewersjya
bugs1299515
milestone60.0a1
Bug 1299515 - Implement MediaTimer::Cancel to allow for rejecting timer promises. r?jya MozReview-Commit-ID: ESLlbIS8XHa
dom/media/MediaTimer.cpp
dom/media/MediaTimer.h
--- a/dom/media/MediaTimer.cpp
+++ b/dom/media/MediaTimer.cpp
@@ -52,22 +52,20 @@ MediaTimer::DispatchDestroy()
 }
 
 void
 MediaTimer::Destroy()
 {
   MOZ_ASSERT(OnMediaTimerThread());
   TIMER_LOG("MediaTimer::Destroy");
 
-  // Reject any outstanding entries. There's no need to acquire the monitor
-  // here, because we're on the timer thread and all other references to us
-  // must be gone.
-  while (!mEntries.empty()) {
-    mEntries.top().mPromise->Reject(false, __func__);
-    mEntries.pop();
+  // Reject any outstanding entries.
+  {
+    MonitorAutoLock lock(mMonitor);
+    Reject();
   }
 
   // Cancel the timer if necessary.
   CancelTimerIfArmed();
 
   delete this;
 }
 
@@ -93,16 +91,24 @@ MediaTimer::WaitUntil(const TimeStamp& a
   Entry e(aTimeStamp, aCallSite);
   RefPtr<MediaTimerPromise> p = e.mPromise.get();
   mEntries.push(e);
   ScheduleUpdate();
   return p;
 }
 
 void
+MediaTimer::Cancel()
+{
+  MonitorAutoLock mon(mMonitor);
+  TIMER_LOG("MediaTimer::Cancel");
+  Reject();
+}
+
+void
 MediaTimer::ScheduleUpdate()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (mUpdateScheduled) {
     return;
   }
   mUpdateScheduled = true;
 
@@ -160,16 +166,26 @@ MediaTimer::UpdateLocked()
 
   // We've got more entries - (re)arm the timer for the soonest one.
   if (!TimerIsArmed() || mEntries.top().mTimeStamp < mCurrentTimerTarget) {
     CancelTimerIfArmed();
     ArmTimer(mEntries.top().mTimeStamp, now);
   }
 }
 
+void
+MediaTimer::Reject()
+{
+  mMonitor.AssertCurrentThreadOwns();
+  while (!mEntries.empty()) {
+    mEntries.top().mPromise->Reject(false, __func__);
+    mEntries.pop();
+  }
+}
+
 /*
  * We use a callback function, rather than a callback method, to ensure that
  * the nsITimer does not artifically keep the refcount of the MediaTimer above
  * zero. When the MediaTimer is destroyed, it safely cancels the nsITimer so that
  * we never fire against a dangling closure.
  */
 
 /* static */ void
--- a/dom/media/MediaTimer.h
+++ b/dom/media/MediaTimer.h
@@ -39,28 +39,30 @@ public:
   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> WaitFor(const TimeDuration& aDuration, const char* aCallSite);
   RefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite);
+  void Cancel(); // Cancel and reject any unresolved promises with false.
 
 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();
   bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow);
+  void Reject();
 
   static void TimerCallback(nsITimer* aTimer, void* aClosure);
   void TimerFired();
   void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);
 
   bool TimerIsArmed()
   {
     return !mCurrentTimerTarget.IsNull();