Bug 1371020 P1 Avoid rescheduling the TimeoutExecutor in ClearTimeout() in some cases. r=ehsan
authorBen Kelly <ben@wanderview.com>
Thu, 08 Jun 2017 19:13:36 -0700
changeset 413628 d832f5aee62a3eba4ef5b1e24f4713d837790710
parent 413627 40a8be84033f3a45c3da1ef3f3f8f910c51bb74d
child 413629 536ad9812d0c41f51472fffaf75995fc459da4ad
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1371020
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1371020 P1 Avoid rescheduling the TimeoutExecutor in ClearTimeout() in some cases. r=ehsan
dom/base/TimeoutManager.cpp
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -486,48 +486,57 @@ TimeoutManager::SetTimeout(nsITimeoutHan
 }
 
 void
 TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
 {
   uint32_t timerId = (uint32_t)aTimerId;
 
   bool firstTimeout = true;
+  bool deferredDeletion = false;
 
   ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) {
     MOZ_LOG(gLog, LogLevel::Debug,
             ("Clear%s(TimeoutManager=%p, timeout=%p, aTimerId=%u, ID=%u, tracking=%d)\n", aTimeout->mIsInterval ? "Interval" : "Timeout",
              this, aTimeout, timerId, aTimeout->mTimeoutId,
              int(aTimeout->mIsTracking)));
 
     if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) {
       if (aTimeout->mRunning) {
         /* We're running from inside the aTimeout. Mark this
            aTimeout for deferred deletion by the code in
            RunTimeout() */
         aTimeout->mIsInterval = false;
+        deferredDeletion = true;
       }
       else {
         /* Delete the aTimeout from the pending aTimeout list */
         aTimeout->remove();
       }
       return true; // abort!
     }
 
     firstTimeout = false;
 
     return false;
   });
 
-  if (!firstTimeout) {
+  // We don't need to reschedule the executor if any of the following are true:
+  //  * If the we weren't cancelling the first timeout, then the executor's
+  //    state doesn't need to change.  It will only reflect the next soonest
+  //    Timeout.
+  //  * If we did cancel the first Timeout, but its currently running, then
+  //    RunTimeout() will handle rescheduling the executor.
+  //  * If the window has become suspended then we should not start executing
+  //    Timeouts.
+  if (!firstTimeout || deferredDeletion || mWindow.IsSuspended()) {
     return;
   }
 
-  // If the first timeout was cancelled we need to stop the executor and
-  // restart at the next soonest deadline.
+  // Stop the executor and restart it at the next soonest deadline.
   mExecutor->Cancel();
 
   OrderedTimeoutIterator iter(mNormalTimeouts, mTrackingTimeouts);
   Timeout* nextTimeout = iter.Next();
   if (nextTimeout) {
     MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(nextTimeout->When()));
   }
 }