зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1343912 P4 Only execute consecutive timeout handlers for a limit period of time. r=ehsan
This commit is contained in:
Родитель
2473d25f1f
Коммит
07eac0b288
|
@ -245,6 +245,11 @@ uint32_t TimeoutManager::sNestingLevel = 0;
|
|||
|
||||
namespace {
|
||||
|
||||
// The maximum number of milliseconds to allow consecutive timer callbacks
|
||||
// to run in a single event loop runnable.
|
||||
#define DEFAULT_MAX_CONSECUTIVE_CALLBACKS_MILLISECONDS 4
|
||||
uint32_t gMaxConsecutiveCallbacksMilliseconds;
|
||||
|
||||
// The maximum number of timer callbacks we will try to run in a single event
|
||||
// loop runnable.
|
||||
#define DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS 5
|
||||
|
@ -366,6 +371,10 @@ TimeoutManager::Initialize()
|
|||
Preferences::AddUintVarCache(&gTargetMaxConsecutiveCallbacks,
|
||||
"dom.timeout.max_consecutive_callbacks",
|
||||
DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS);
|
||||
|
||||
Preferences::AddUintVarCache(&gMaxConsecutiveCallbacksMilliseconds,
|
||||
"dom.timeout.max_consecutive_callbacks_ms",
|
||||
DEFAULT_MAX_CONSECUTIVE_CALLBACKS_MILLISECONDS);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
@ -589,6 +598,23 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
|
||||
NS_ASSERTION(!mWindow.IsFrozen(), "Timeout running on a window in the bfcache!");
|
||||
|
||||
// Limit the overall time spent in RunTimeout() to reduce jank.
|
||||
uint32_t totalTimeLimitMS = std::max(1u, gMaxConsecutiveCallbacksMilliseconds);
|
||||
const TimeDuration totalTimeLimit = TimeDuration::FromMilliseconds(totalTimeLimitMS);
|
||||
|
||||
// Allow up to 25% of our total time budget to be used figuring out which
|
||||
// timers need to run. This is the initial loop in this method.
|
||||
const TimeDuration initalTimeLimit =
|
||||
TimeDuration::FromMilliseconds(totalTimeLimit.ToMilliseconds() / 4);
|
||||
|
||||
// Ammortize overhead from from calling TimeStamp::Now() in the initial
|
||||
// loop, though, by only checking for an elapsed limit every N timeouts.
|
||||
const uint32_t kNumTimersPerInitialElapsedCheck = 100;
|
||||
|
||||
// Start measuring elapsed time immediately. We won't potentially expire
|
||||
// the time budget until at least one Timeout has run, though.
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
|
||||
Timeout* last_expired_normal_timeout = nullptr;
|
||||
Timeout* last_expired_tracking_timeout = nullptr;
|
||||
bool last_expired_timeout_is_normal = false;
|
||||
|
@ -677,11 +703,23 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
// trust chrome windows not to misbehave and partly because a
|
||||
// number of browser chrome tests have races that depend on this
|
||||
// coalescing.
|
||||
if (targetTimerSeen &&
|
||||
numTimersToRun >= gTargetMaxConsecutiveCallbacks &&
|
||||
//
|
||||
// Chrome windows are still subject to our time budget limit,
|
||||
// however. The time budget allows many timers to coallesce and
|
||||
// chrome script should not hit this limit under normal
|
||||
// circumstances.
|
||||
if (targetTimerSeen) {
|
||||
if (numTimersToRun >= gTargetMaxConsecutiveCallbacks &&
|
||||
!mWindow.IsChromeWindow()) {
|
||||
break;
|
||||
}
|
||||
if (numTimersToRun % kNumTimersPerInitialElapsedCheck == 0) {
|
||||
TimeDuration elapsed(TimeStamp::Now() - start);
|
||||
if (elapsed >= initalTimeLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expiredIter.UpdateIterator();
|
||||
|
@ -734,6 +772,8 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
mTrackingTimeouts.SetInsertionPoint(dummy_tracking_timeout);
|
||||
}
|
||||
|
||||
bool targetTimeoutSeen = false;
|
||||
|
||||
// We stop iterating each list when we go past the last expired timeout from
|
||||
// that list that we have observed above. That timeout will either be the
|
||||
// dummy timeout for the list that the last expired timeout came from, or it
|
||||
|
@ -789,6 +829,10 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (timeout == aTimeout) {
|
||||
targetTimeoutSeen = true;
|
||||
}
|
||||
|
||||
// This timeout is good to run
|
||||
bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
|
@ -854,6 +898,18 @@ TimeoutManager::RunTimeout(Timeout* aTimeout)
|
|||
|
||||
// Release the timeout struct since it's possibly out of the list
|
||||
timeout->Release();
|
||||
|
||||
// Check to see if we have run out of time to execute timeout handlers.
|
||||
// If we've exceeded our time budget then terminate the loop immediately.
|
||||
//
|
||||
// Note, we only do this if we have seen the Timeout object explicitly
|
||||
// passed to RunTimeout(). The target timeout must always be executed.
|
||||
if (targetTimeoutSeen) {
|
||||
TimeDuration elapsed = TimeStamp::Now() - start;
|
||||
if (elapsed >= totalTimeLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5721,6 +5721,10 @@ pref("dom.moduleScripts.enabled", false);
|
|||
// event loop runnable. Minimum value of 1.
|
||||
pref("dom.timeout.max_consecutive_callbacks", 5);
|
||||
|
||||
// Maximum amount of time in milliseconds consecutive setTimeout()/setInterval()
|
||||
// callback are allowed to run before yielding the event loop.
|
||||
pref("dom.timeout.max_consecutive_callbacks_ms", 4);
|
||||
|
||||
#ifdef FUZZING
|
||||
pref("fuzzing.enabled", false);
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче