author | Ben Kelly <ben@wanderview.com> |
Mon, 27 Feb 2017 17:33:30 -0500 | |
changeset 345147 | e1a939a64f4b0ce63d1879705685c866f4297b36 |
parent 345146 | 4ee8c67b75bc1e300ffa5376198b1b991d0928e7 |
child 345148 | cf4daf78f4797ef07e13729c8f3913af9576ca3b |
push id | 38073 |
push user | cbook@mozilla.com |
push date | Tue, 28 Feb 2017 12:04:44 +0000 |
treeherder | autoland@0d6ca3d14e5b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1342854 |
milestone | 54.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
|
--- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -66,16 +66,21 @@ static int32_t gTimeoutBucketingStrategy // timer code can handle, really. See DELAY_INTERVAL_LIMIT in // nsTimerImpl.h for details. #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT uint32_t TimeoutManager::sNestingLevel = 0; namespace { +// The maximum number of timer callbacks we will try to run in a single event +// loop runnable. +#define DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS 5 +uint32_t gTargetMaxConsecutiveCallbacks; + // The number of queued runnables within the TabGroup ThrottledEventQueue // at which to begin applying back pressure to the window. #define DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE 5000 static uint32_t gThrottledEventQueueBackPressure; // The amount of delay to apply to timers when back pressure is triggered. // As the length of the ThrottledEventQueue grows delay is increased. The // delay is scaled such that every kThrottledEventQueueBackPressure runnables @@ -177,16 +182,20 @@ TimeoutManager::Initialize() "dom.timeout.back_pressure_delay_ms", DEFAULT_BACK_PRESSURE_DELAY_MS); Preferences::AddUintVarCache(&gBackPressureDelayReductionThresholdMS, "dom.timeout.back_pressure_delay_reduction_threshold_ms", DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS); Preferences::AddUintVarCache(&gBackPressureDelayMinimumMS, "dom.timeout.back_pressure_delay_minimum_ms", DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS); + + Preferences::AddUintVarCache(&gTargetMaxConsecutiveCallbacks, + "dom.timeout.max_consecutive_callbacks", + DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS); } uint32_t TimeoutManager::GetTimeoutId(Timeout::Reason aReason) { switch (aReason) { case Timeout::Reason::eIdleCallbackTimeout: return ++mIdleCallbackTimeoutCounter; @@ -439,16 +448,20 @@ TimeoutManager::RunTimeout(Timeout* aTim // nothing past that point is expired. { // Use a nested scope in order to make sure the strong references held by // the iterator are freed after the loop. OrderedTimeoutIterator expiredIter(mNormalTimeouts, mTrackingTimeouts, nullptr, nullptr); + + uint32_t numTimersToRun = 0; + bool targetTimerSeen = false; + while (true) { Timeout* timeout = expiredIter.Next(); if (!timeout || timeout->When() > deadline) { break; } if (timeout->mFiringDepth == 0) { // Mark any timeouts that are on the list to be fired with the @@ -456,29 +469,39 @@ TimeoutManager::RunTimeout(Timeout* aTim timeout->mFiringDepth = firingDepth; last_expired_timeout_is_normal = expiredIter.PickedNormalIter(); if (last_expired_timeout_is_normal) { last_expired_normal_timeout = timeout; } else { last_expired_tracking_timeout = timeout; } - // Run available timers until we see our target timer. After - // that, however, stop coalescing timers so we can yield the - // main thread. Further timers that are ready will get picked - // up by their own nsITimer runnables when they execute. + // Note that we have seen our target timer. This means we can now + // stop processing timers once we hit our threshold below. + if (timeout == aTimeout) { + targetTimerSeen = true; + } + + // Run only a limited number of timers based on the configured + // maximum. Note, we must always run our target timer however. + // Further timers that are ready will get picked up by their own + // nsITimer runnables when they execute. // // For chrome windows, however, we do coalesce all timers and // do not yield the main thread. This is partly because we // trust chrome windows not to misbehave and partly because a // number of browser chrome tests have races that depend on this // coalescing. - if (timeout == aTimeout && !mWindow.IsChromeWindow()) { + if (targetTimerSeen && + numTimersToRun >= gTargetMaxConsecutiveCallbacks && + !mWindow.IsChromeWindow()) { break; } + + numTimersToRun += 1; } expiredIter.UpdateIterator(); } } // Maybe the timeout that the event was fired for has been deleted // and there are no others timeouts with deadlines that make them