Bug 1378586 P2 Avoid Timeout mNestingLevel rollover by just limiting the value to the values we care about. r=farre
authorBen Kelly <ben@wanderview.com>
Tue, 25 Jul 2017 08:35:12 -0700
changeset 419595 adc6dc3c8c4e99dc7d50174df4a6b40b5cade227
parent 419594 ef429397347b94645f29765e28559f0f46a64858
child 419596 9d1d8507fc97fde906fff564a29e85981526f246
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1378586
milestone56.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 1378586 P2 Avoid Timeout mNestingLevel rollover by just limiting the value to the values we care about. r=farre
dom/base/Timeout.h
dom/base/TimeoutManager.cpp
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -74,17 +74,19 @@ public:
 
   // Interval
   TimeDuration mInterval;
 
   // Identifies which firing level this Timeout is being processed in
   // when sync loops trigger nested firing.
   uint32_t mFiringId;
 
-  uint32_t mNestingLevel;
+  // Between 0 and DOM_CLAMP_TIMEOUT_NESTING_LEVEL.  Currently we don't
+  // care about nesting levels beyond that value.
+  uint8_t mNestingLevel;
 
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupControlState mPopupState;
 
   // The language-specific information about the callback.
   nsCOMPtr<nsITimeoutHandler> mScriptHandler;
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -304,17 +304,17 @@ TimeoutManager::IsInvalidFiringId(uint32
   // should be rare.  It can only happen with deeply nested event
   // loop spinning.  For example, a page that does a lot of timers
   // and a lot of sync XHRs within those timers could be slow here.
   return !mFiringIdStack.Contains(aFiringId);
 }
 
 // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
 // uses 5.
-#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
+#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5u
 
 TimeDuration
 TimeoutManager::CalculateDelay(Timeout* aTimeout) const {
   MOZ_DIAGNOSTIC_ASSERT(aTimeout);
   TimeDuration result = aTimeout->mInterval;
 
   if (aTimeout->mIsInterval ||
       aTimeout->mNestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
@@ -587,17 +587,18 @@ TimeoutManager::SetTimeout(nsITimeoutHan
     timeout->mIsTracking = (rand() % 2) == 0;
 
     MOZ_LOG(gLog, LogLevel::Debug,
             ("Classified timeout %p as %stracking (random mode)\n",
              timeout.get(), timeout->mIsTracking ? "" : "non-"));
     break;
   }
 
-  timeout->mNestingLevel = sNestingLevel + 1;
+  timeout->mNestingLevel = sNestingLevel < DOM_CLAMP_TIMEOUT_NESTING_LEVEL
+                         ? sNestingLevel + 1 : sNestingLevel;
 
   // Now clamp the actual interval we will use for the timer based on
   TimeDuration realInterval = CalculateDelay(timeout);
   TimeStamp now = TimeStamp::Now();
   timeout->SetWhenOrTimeRemaining(now, realInterval);
 
   // If we're not suspended, then set the timer.
   if (!mWindow.IsSuspended()) {
@@ -980,17 +981,19 @@ TimeoutManager::RescheduleTimeout(Timeou
   MOZ_DIAGNOSTIC_ASSERT(aLastCallbackTime <= aCurrentNow);
 
   if (!aTimeout->mIsInterval) {
     return false;
   }
 
   // Automatically increase the nesting level when a setInterval()
   // is rescheduled just as if it was using a chained setTimeout().
-  aTimeout->mNestingLevel += 1;
+  if (aTimeout->mNestingLevel < DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
+    aTimeout->mNestingLevel += 1;
+  }
 
   // Compute time to next timeout for interval timer.
   // Make sure nextInterval is at least CalculateDelay().
   TimeDuration nextInterval = CalculateDelay(aTimeout);
 
   TimeStamp firingTime = aLastCallbackTime + nextInterval;
   TimeDuration delay = firingTime - aCurrentNow;