Bug 1300659 P3 Make setTimeout() and setInterval() use the TabGroup ThrottledEventQueue. r=smaug

This commit is contained in:
Ben Kelly 2016-11-07 12:30:17 -08:00
parent 18e9d9bbcc
commit 595fb0d20b
4 changed files with 53 additions and 26 deletions

View File

@ -55,18 +55,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Timeout, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Timeout, Release)
nsresult
Timeout::InitTimer(uint32_t aDelay)
namespace {
void
TimerCallback(nsITimer*, void* aClosure)
{
return mTimer->InitWithNameableFuncCallback(
nsGlobalWindow::TimerCallback, this, aDelay,
nsITimer::TYPE_ONE_SHOT, Timeout::TimerNameCallback);
RefPtr<Timeout> timeout = (Timeout*)aClosure;
timeout->mWindow->RunTimeout(timeout);
}
// static
void
Timeout::TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
size_t aLen)
TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf, size_t aLen)
{
RefPtr<Timeout> timeout = (Timeout*)aClosure;
@ -76,6 +75,28 @@ Timeout::TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
snprintf(aBuf, aLen, "[content] %s:%u:%u", filename, lineNum, column);
}
} // anonymous namespace
nsresult
Timeout::InitTimer(nsIEventTarget* aTarget, uint32_t aDelay)
{
// If the given target does not match the timer's current target
// then we need to override it before the Init. Note that GetTarget()
// will return the current thread after setting the target to nullptr.
// So we need to special case the nullptr target comparison.
nsCOMPtr<nsIEventTarget> currentTarget;
MOZ_ALWAYS_SUCCEEDS(mTimer->GetTarget(getter_AddRefs(currentTarget)));
if ((aTarget && currentTarget != aTarget) ||
(!aTarget && currentTarget != NS_GetCurrentThread())) {
// Always call Cancel() in case we are re-using a timer. Otherwise
// the subsequent SetTarget() may fail.
MOZ_ALWAYS_SUCCEEDS(mTimer->Cancel());
MOZ_ALWAYS_SUCCEEDS(mTimer->SetTarget(aTarget));
}
return mTimer->InitWithNameableFuncCallback(
TimerCallback, this, aDelay, nsITimer::TYPE_ONE_SHOT, TimerNameCallback);
}
// Return true if this timeout has a refcount of 1. This is used to check
// that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.

View File

@ -34,13 +34,13 @@ public:
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
nsresult InitTimer(uint32_t aDelay);
// The target may be specified to use a particular event queue for the
// resulting timer runnable. A nullptr target will result in the
// default main thread being used.
nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
static void TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
size_t aLen);
#ifdef DEBUG
bool HasRefCntOne() const;
#endif // DEBUG

View File

@ -11882,7 +11882,7 @@ nsGlobalWindow::Resume()
continue;
}
nsresult rv = t->InitTimer(delay);
nsresult rv = t->InitTimer(GetThrottledEventQueue(), delay);
if (NS_FAILED(rv)) {
t->mTimer = nullptr;
t->remove();
@ -12601,7 +12601,7 @@ nsGlobalWindow::SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
RefPtr<Timeout> copy = timeout;
rv = timeout->InitTimer(realInterval);
rv = timeout->InitTimer(GetThrottledEventQueue(), realInterval);
if (NS_FAILED(rv)) {
return rv;
}
@ -12874,7 +12874,8 @@ nsGlobalWindow::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now,
// Reschedule the OS timer. Don't bother returning any error codes if
// this fails since the callers of this method don't care about them.
nsresult rv = aTimeout->InitTimer(delay.ToMilliseconds());
nsresult rv = aTimeout->InitTimer(GetThrottledEventQueue(),
delay.ToMilliseconds());
if (NS_FAILED(rv)) {
NS_ERROR("Error initializing timer for DOM timeout!");
@ -12949,6 +12950,20 @@ nsGlobalWindow::RunTimeout(Timeout* aTimeout)
// firing depth so that we can reentrantly run timeouts
timeout->mFiringDepth = firingDepth;
last_expired_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.
//
// 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 && !IsChromeWindow()) {
break;
}
}
}
@ -13167,7 +13182,8 @@ nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
timeout->mFiringDepth = firingDepth;
timeout->Release();
nsresult rv = timeout->InitTimer(delay.ToMilliseconds());
nsresult rv = timeout->InitTimer(GetThrottledEventQueue(),
delay.ToMilliseconds());
if (NS_FAILED(rv)) {
NS_WARNING("Error resetting non background timer for DOM timeout!");
@ -13256,15 +13272,6 @@ nsGlobalWindow::InsertTimeoutIntoList(Timeout* aTimeout)
aTimeout->AddRef();
}
// static
void
nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
{
RefPtr<Timeout> timeout = (Timeout*)aClosure;
timeout->mWindow->RunTimeout(timeout);
}
//*****************************************************************************
// nsGlobalWindow: Helper Functions
//*****************************************************************************

View File

@ -1498,7 +1498,6 @@ public:
// Insert aTimeout into the list, before all timeouts that would
// fire after it, but no earlier than mTimeoutInsertionPoint, if any.
void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
static void TimerCallback(nsITimer *aTimer, void *aClosure);
uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
// Helper Functions