Bug 1323202 - Factor out the timeouts linked list into a separate data structure; r=bkelly
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 13 Dec 2016 10:23:03 -0500
changeset 449469 fda9283e646db33b9ef2b936ed878108da40c0e6
parent 449438 6f6c33db62e18c6c9d6d4223f7b19406147a9b51
child 449470 8508fca15e2f0f36b0a5f881180c876e4747d105
push id38569
push userbmo:cam@mcc.id.au
push dateWed, 14 Dec 2016 06:38:58 +0000
reviewersbkelly
bugs1323202
milestone53.0a1
Bug 1323202 - Factor out the timeouts linked list into a separate data structure; r=bkelly This is in preparation of splitting the timeouts list into normal and tracking timeouts.
dom/base/TimeoutManager.cpp
dom/base/TimeoutManager.h
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -71,17 +71,16 @@ CalculateNewBackPressureDelayMS(uint32_t
   }
   return static_cast<int32_t>(value);
 }
 
 } // anonymous namespace
 
 TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
   : mWindow(aWindow),
-    mTimeoutInsertionPoint(nullptr),
     mTimeoutIdCounter(1),
     mTimeoutFiringDepth(0),
     mRunningTimeout(nullptr),
     mIdleCallbackTimeoutCounter(1),
     mBackPressureDelayMS(0)
 {
   MOZ_DIAGNOSTIC_ASSERT(aWindow.IsInnerWindow());
 }
@@ -221,17 +220,17 @@ TimeoutManager::SetTimeout(nsITimeoutHan
 }
 
 void
 TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
 {
   uint32_t timerId = (uint32_t)aTimerId;
   Timeout* timeout;
 
-  for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
+  for (timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) {
     if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
       if (timeout->mRunning) {
         /* We're running from inside the timeout. Mark this
            timeout for deferred deletion by the code in
            RunTimeout() */
         timeout->mIsInterval = false;
       }
       else {
@@ -291,17 +290,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
 
   // The timeout list is kept in deadline order. Discover the latest timeout
   // whose deadline has expired. On some platforms, native timeout events fire
   // "early", but we handled that above by setting deadline to aTimeout->mWhen
   // if the timer fired early.  So we can stop walking if we get to timeouts
   // whose mWhen is greater than deadline, since once that happens we know
   // nothing past that point is expired.
   last_expired_timeout = nullptr;
-  for (Timeout* timeout = mTimeouts.getFirst();
+  for (Timeout* timeout = mTimeouts.GetFirst();
        timeout && timeout->mWhen <= deadline;
        timeout = timeout->getNext()) {
     if (timeout->mFiringDepth == 0) {
       // Mark any timeouts that are on the list to be fired with the
       // firing depth so that we can reentrantly run timeouts
       timeout->mFiringDepth = firingDepth;
       last_expired_timeout = timeout;
 
@@ -334,22 +333,22 @@ TimeoutManager::RunTimeout(Timeout* aTim
   // win_run_timeout(). This dummy timeout serves as the head of the
   // list for any timeouts inserted as a result of running a timeout.
   RefPtr<Timeout> dummy_timeout = new Timeout();
   dummy_timeout->mFiringDepth = firingDepth;
   dummy_timeout->mWhen = now;
   last_expired_timeout->setNext(dummy_timeout);
   RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
 
-  last_insertion_point = mTimeoutInsertionPoint;
-  // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
-  // the logic in ResetTimersForThrottleReduction will need to change.
-  mTimeoutInsertionPoint = dummy_timeout;
+  last_insertion_point = mTimeouts.InsertionPoint();
+  // If we ever start setting insertion point to a non-dummy timeout, the logic
+  // in ResetTimersForThrottleReduction will need to change.
+  mTimeouts.SetInsertionPoint(dummy_timeout);
 
-  for (Timeout* timeout = mTimeouts.getFirst();
+  for (Timeout* timeout = mTimeouts.GetFirst();
        timeout != dummy_timeout && !mWindow.IsFrozen();
        timeout = nextTimeout) {
     nextTimeout = timeout->getNext();
 
     if (timeout->mFiringDepth != firingDepth) {
       // We skip the timeout since it's on the list to run at another
       // depth.
 
@@ -382,17 +381,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
     if (timeout_was_cleared) {
       // The running timeout's window was cleared, this means that
       // ClearAllTimeouts() was called from a *nested* call, possibly
       // through a timeout that fired while a modal (to this window)
       // dialog was open or through other non-obvious paths.
       MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
       Unused << timeoutExtraRef.forget().take();
 
-      mTimeoutInsertionPoint = last_insertion_point;
+      mTimeouts.SetInsertionPoint(last_insertion_point);
 
       return;
     }
 
     // If we have a regular interval timer, we re-schedule the
     // timeout, accounting for clock drift.
     bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
 
@@ -412,17 +411,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
     timeout->Release();
   }
 
   // Take the dummy timeout off the head of the list
   dummy_timeout->remove();
   timeoutExtraRef = nullptr;
   MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
 
-  mTimeoutInsertionPoint = last_insertion_point;
+  mTimeouts.SetInsertionPoint(last_insertion_point);
 
   MaybeApplyBackPressure();
 }
 
 void
 TimeoutManager::MaybeApplyBackPressure()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -593,25 +592,25 @@ TimeoutManager::ResetTimersForThrottleRe
   MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
 
   if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
     return NS_OK;
   }
 
   TimeStamp now = TimeStamp::Now();
 
-  // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
-  // timers and the timers we're planning to fire all come before
-  // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
-  // with an mWhen that may be semi-bogus.  In that case, we don't need to do
-  // anything with mTimeoutInsertionPoint or anything before it, so should
-  // start at the timer after mTimeoutInsertionPoint, if there is one.
+  // If insertion point is non-null, we're in the middle of firing timers and
+  // the timers we're planning to fire all come before insertion point;
+  // insertion point itself is a dummy timeout with an mWhen that may be
+  // semi-bogus.  In that case, we don't need to do anything with insertion
+  // point or anything before it, so should start at the timer after insertion
+  // point, if there is one.
   // Otherwise, start at the beginning of the list.
-  for (Timeout* timeout = mTimeoutInsertionPoint ?
-         mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
+  for (Timeout* timeout = mTimeouts.InsertionPoint() ?
+         mTimeouts.InsertionPoint()->getNext() : mTimeouts.GetFirst();
        timeout; ) {
     // It's important that this check be <= so that we guarantee that
     // taking std::max with |now| won't make a quantity equal to
     // timeout->mWhen below.
     if (timeout->mWhen <= now) {
       timeout = timeout->getNext();
       continue;
     }
@@ -682,24 +681,25 @@ TimeoutManager::ResetTimersForThrottleRe
 }
 
 void
 TimeoutManager::ClearAllTimeouts()
 {
   Timeout* timeout;
   Timeout* nextTimeout;
 
-  for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
+  for (timeout = mTimeouts.GetFirst(); timeout; timeout = nextTimeout) {
     /* If RunTimeout() is higher up on the stack for this
        window, e.g. as a result of document.write from a timeout,
        then we need to reset the list insertion point for
        newly-created timeouts in case the user adds a timeout,
        before we pop the stack back to RunTimeout. */
-    if (mRunningTimeout == timeout)
-      mTimeoutInsertionPoint = nullptr;
+    if (mRunningTimeout == timeout) {
+      mTimeouts.SetInsertionPoint(nullptr);
+    }
 
     nextTimeout = timeout->getNext();
 
     if (timeout->mTimer) {
       timeout->mTimer->Cancel();
       timeout->mTimer = nullptr;
 
       // Drop the count since the timer isn't going to hold on
@@ -711,42 +711,41 @@ TimeoutManager::ClearAllTimeouts()
     // cleared and taken out of the list of timeouts
     timeout->mCleared = true;
 
     // Drop the count since we're removing it from the list.
     timeout->Release();
   }
 
   // Clear out our list
-  mTimeouts.clear();
+  mTimeouts.Clear();
 }
 
 void
 TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout)
 {
-  // Start at mLastTimeout and go backwards.  Don't go further than
-  // mTimeoutInsertionPoint, though.  This optimizes for the common case of
-  // insertion at the end.
+  // Start at mLastTimeout and go backwards.  Don't go further than insertion
+  // point, though.  This optimizes for the common case of insertion at the end.
   Timeout* prevSibling;
-  for (prevSibling = mTimeouts.getLast();
-       prevSibling && prevSibling != mTimeoutInsertionPoint &&
+  for (prevSibling = mTimeouts.GetLast();
+       prevSibling && prevSibling != mTimeouts.InsertionPoint() &&
          // This condition needs to match the one in SetTimeoutOrInterval that
          // determines whether to set mWhen or mTimeRemaining.
          (mWindow.IsFrozen() ?
           prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
           prevSibling->mWhen > aTimeout->mWhen);
        prevSibling = prevSibling->getPrevious()) {
     /* Do nothing; just searching */
   }
 
   // Now link in aTimeout after prevSibling.
   if (prevSibling) {
     prevSibling->setNext(aTimeout);
   } else {
-    mTimeouts.insertFront(aTimeout);
+    mTimeouts.InsertFront(aTimeout);
   }
 
   aTimeout->mFiringDepth = 0;
 
   // Increment the timeout's reference count since it's now held on to
   // by the list
   aTimeout->AddRef();
 }
@@ -770,29 +769,29 @@ TimeoutManager::EndRunningTimeout(Timeou
   --gRunningTimeoutDepth;
 
   mRunningTimeout = aTimeout;
 }
 
 void
 TimeoutManager::UnmarkGrayTimers()
 {
-  for (Timeout* timeout = mTimeouts.getFirst();
+  for (Timeout* timeout = mTimeouts.GetFirst();
        timeout;
        timeout = timeout->getNext()) {
     if (timeout->mScriptHandler) {
       timeout->mScriptHandler->MarkForCC();
     }
   }
 }
 
 void
 TimeoutManager::Suspend()
 {
-  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+  for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
     // Leave the timers with the current time remaining.  This will
     // cause the timers to potentially fire when the window is
     // Resume()'d.  Time effectively passes while suspended.
 
     // Drop the XPCOM timer; we'll reschedule when restoring the state.
     if (t->mTimer) {
       t->mTimer->Cancel();
       t->mTimer = nullptr;
@@ -805,17 +804,17 @@ TimeoutManager::Suspend()
 }
 
 void
 TimeoutManager::Resume()
 {
   TimeStamp now = TimeStamp::Now();
   DebugOnly<bool> _seenDummyTimeout = false;
 
-  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+  for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
     // There's a chance we're being called with RunTimeout on the stack in which
     // case we have a dummy timeout in the list that *must not* be resumed. It
     // can be identified by a null mWindow.
     if (!t->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       continue;
     }
@@ -851,17 +850,17 @@ TimeoutManager::Resume()
     t->AddRef();
   }
 }
 
 void
 TimeoutManager::Freeze()
 {
   TimeStamp now = TimeStamp::Now();
-  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+  for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
     // Save the current remaining time for this timeout.  We will
     // re-apply it when the window is Thaw()'d.  This effectively
     // shifts timers to the right as if time does not pass while
     // the window is frozen.
     if (t->mWhen > now) {
       t->mTimeRemaining = t->mWhen - now;
     } else {
       t->mTimeRemaining = TimeDuration(0);
@@ -874,17 +873,17 @@ TimeoutManager::Freeze()
 }
 
 void
 TimeoutManager::Thaw()
 {
   TimeStamp now = TimeStamp::Now();
   DebugOnly<bool> _seenDummyTimeout = false;
 
-  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+  for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
     // There's a chance we're being called with RunTimeout on the stack in which
     // case we have a dummy timeout in the list that *must not* be resumed. It
     // can be identified by a null mWindow.
     if (!t->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       continue;
     }
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -23,17 +23,17 @@ public:
   TimeoutManager(const TimeoutManager& rhs) = delete;
   void operator=(const TimeoutManager& rhs) = delete;
 
   bool IsRunningTimeout() const { return mTimeoutFiringDepth > 0; }
 
   static uint32_t GetNestingLevel() { return sNestingLevel; }
   static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; }
 
-  bool HasTimeouts() const { return !mTimeouts.isEmpty(); }
+  bool HasTimeouts() const { return !mTimeouts.IsEmpty(); }
 
   nsresult SetTimeout(nsITimeoutHandler* aHandler,
                       int32_t interval, bool aIsInterval,
                       mozilla::dom::Timeout::Reason aReason,
                       int32_t* aReturn);
   void ClearTimeout(int32_t aTimerId,
                     mozilla::dom::Timeout::Reason aReason);
 
@@ -82,39 +82,67 @@ public:
 
   // Initialize TimeoutManager before the first time it is accessed.
   static void Initialize();
 
   // Run some code for each Timeout in our list.
   template <class Callable>
   void ForEachTimeout(Callable c)
   {
-    for (Timeout* timeout = mTimeouts.getFirst();
+    for (Timeout* timeout = mTimeouts.GetFirst();
          timeout;
          timeout = timeout->getNext()) {
       c(timeout);
     }
   }
 
 private:
   nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS);
 
 private:
+  typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList;
+  struct Timeouts {
+    Timeouts()
+      : mTimeoutInsertionPoint(nullptr)
+    {
+    }
+
+    const Timeout* GetFirst() const { return mTimeoutList.getFirst(); }
+    Timeout* GetFirst() { return mTimeoutList.getFirst(); }
+    const Timeout* GetLast() const { return mTimeoutList.getLast(); }
+    Timeout* GetLast() { return mTimeoutList.getLast(); }
+    bool IsEmpty() const { return mTimeoutList.isEmpty(); }
+    void InsertFront(Timeout* aTimeout) { mTimeoutList.insertFront(aTimeout); }
+    void Clear() { mTimeoutList.clear(); }
+
+    void SetInsertionPoint(Timeout* aTimeout)
+    {
+      mTimeoutInsertionPoint = aTimeout;
+    }
+    Timeout* InsertionPoint()
+    {
+      return mTimeoutInsertionPoint;
+    }
+
+  private:
+    // mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is
+    // non-null.  In that case, the dummy timeout pointed to by
+    // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
+    // that come after it.
+    TimeoutList               mTimeoutList;
+    // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
+    // This is a dummy timeout at the moment; if that ever changes, the logic in
+    // ResetTimersForThrottleReduction needs to change.
+    mozilla::dom::Timeout*    mTimeoutInsertionPoint;
+  };
+
   // Each nsGlobalWindow object has a TimeoutManager member.  This reference
   // points to that holder object.
   nsGlobalWindow&             mWindow;
-  // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is
-  // non-null.  In that case, the dummy timeout pointed to by
-  // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
-  // that come after it.
-  mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
-  // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
-  // This is a dummy timeout at the moment; if that ever changes, the logic in
-  // ResetTimersForThrottleReduction needs to change.
-  mozilla::dom::Timeout*      mTimeoutInsertionPoint;
+  Timeouts                    mTimeouts;
   uint32_t                    mTimeoutIdCounter;
   uint32_t                    mTimeoutFiringDepth;
   mozilla::dom::Timeout*      mRunningTimeout;
 
    // The current idle request callback timeout handle
   uint32_t                    mIdleCallbackTimeoutCounter;
 
   int32_t                     mBackPressureDelayMS;