From 595fb0d20bcdf9000cfb02f951a4b383291a984d Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 7 Nov 2016 12:30:17 -0800 Subject: [PATCH] Bug 1300659 P3 Make setTimeout() and setInterval() use the TabGroup ThrottledEventQueue. r=smaug --- dom/base/Timeout.cpp | 37 +++++++++++++++++++++++++++++-------- dom/base/Timeout.h | 8 ++++---- dom/base/nsGlobalWindow.cpp | 33 ++++++++++++++++++++------------- dom/base/nsGlobalWindow.h | 1 - 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/dom/base/Timeout.cpp b/dom/base/Timeout.cpp index b90f896930b3..ebad378e7aca 100644 --- a/dom/base/Timeout.cpp +++ b/dom/base/Timeout.cpp @@ -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*)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*)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 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. diff --git a/dom/base/Timeout.h b/dom/base/Timeout.h index b6f59e410981..9a392cb43145 100644 --- a/dom/base/Timeout.h +++ b/dom/base/Timeout.h @@ -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 diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 66711e3536c3..40a3a4b950be 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -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 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*)aClosure; - - timeout->mWindow->RunTimeout(timeout); -} - //***************************************************************************** // nsGlobalWindow: Helper Functions //***************************************************************************** diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 4f0d3d0c5ade..1507fea7368a 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -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