Bug 1323202 - Factor out the timeouts linked list into a separate data structure; r=bkelly
This is in preparation of splitting the timeouts list into normal and
tracking timeouts.
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -71,17 +71,16 @@ CalculateNewBackPressureDelayMS(uint32_t
}
return static_cast<int32_t>(value);
}
} // anonymous namespace
TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
: mWindow(aWindow),
- mTimeoutInsertionPoint(nullptr),
mTimeoutIdCounter(1),
mTimeoutFiringDepth(0),
mRunningTimeout(nullptr),
mIdleCallbackTimeoutCounter(1),
mBackPressureDelayMS(0)
{
MOZ_DIAGNOSTIC_ASSERT(aWindow.IsInnerWindow());
}
@@ -221,17 +220,17 @@ TimeoutManager::SetTimeout(nsITimeoutHan
}
void
TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
{
uint32_t timerId = (uint32_t)aTimerId;
Timeout* timeout;
- for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
+ for (timeout = mTimeouts.GetFirst(); timeout; timeout = timeout->getNext()) {
if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
if (timeout->mRunning) {
/* We're running from inside the timeout. Mark this
timeout for deferred deletion by the code in
RunTimeout() */
timeout->mIsInterval = false;
}
else {
@@ -291,17 +290,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
// The timeout list is kept in deadline order. Discover the latest timeout
// whose deadline has expired. On some platforms, native timeout events fire
// "early", but we handled that above by setting deadline to aTimeout->mWhen
// if the timer fired early. So we can stop walking if we get to timeouts
// whose mWhen is greater than deadline, since once that happens we know
// nothing past that point is expired.
last_expired_timeout = nullptr;
- for (Timeout* timeout = mTimeouts.getFirst();
+ for (Timeout* timeout = mTimeouts.GetFirst();
timeout && timeout->mWhen <= deadline;
timeout = timeout->getNext()) {
if (timeout->mFiringDepth == 0) {
// Mark any timeouts that are on the list to be fired with the
// firing depth so that we can reentrantly run timeouts
timeout->mFiringDepth = firingDepth;
last_expired_timeout = timeout;
@@ -334,22 +333,22 @@ TimeoutManager::RunTimeout(Timeout* aTim
// win_run_timeout(). This dummy timeout serves as the head of the
// list for any timeouts inserted as a result of running a timeout.
RefPtr<Timeout> dummy_timeout = new Timeout();
dummy_timeout->mFiringDepth = firingDepth;
dummy_timeout->mWhen = now;
last_expired_timeout->setNext(dummy_timeout);
RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
- last_insertion_point = mTimeoutInsertionPoint;
- // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
- // the logic in ResetTimersForThrottleReduction will need to change.
- mTimeoutInsertionPoint = dummy_timeout;
+ last_insertion_point = mTimeouts.InsertionPoint();
+ // If we ever start setting insertion point to a non-dummy timeout, the logic
+ // in ResetTimersForThrottleReduction will need to change.
+ mTimeouts.SetInsertionPoint(dummy_timeout);
- for (Timeout* timeout = mTimeouts.getFirst();
+ for (Timeout* timeout = mTimeouts.GetFirst();
timeout != dummy_timeout && !mWindow.IsFrozen();
timeout = nextTimeout) {
nextTimeout = timeout->getNext();
if (timeout->mFiringDepth != firingDepth) {
// We skip the timeout since it's on the list to run at another
// depth.
@@ -382,17 +381,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
if (timeout_was_cleared) {
// The running timeout's window was cleared, this means that
// ClearAllTimeouts() was called from a *nested* call, possibly
// through a timeout that fired while a modal (to this window)
// dialog was open or through other non-obvious paths.
MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
Unused << timeoutExtraRef.forget().take();
- mTimeoutInsertionPoint = last_insertion_point;
+ mTimeouts.SetInsertionPoint(last_insertion_point);
return;
}
// If we have a regular interval timer, we re-schedule the
// timeout, accounting for clock drift.
bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
@@ -412,17 +411,17 @@ TimeoutManager::RunTimeout(Timeout* aTim
timeout->Release();
}
// Take the dummy timeout off the head of the list
dummy_timeout->remove();
timeoutExtraRef = nullptr;
MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
- mTimeoutInsertionPoint = last_insertion_point;
+ mTimeouts.SetInsertionPoint(last_insertion_point);
MaybeApplyBackPressure();
}
void
TimeoutManager::MaybeApplyBackPressure()
{
MOZ_ASSERT(NS_IsMainThread());
@@ -593,25 +592,25 @@ TimeoutManager::ResetTimersForThrottleRe
MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
return NS_OK;
}
TimeStamp now = TimeStamp::Now();
- // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
- // timers and the timers we're planning to fire all come before
- // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
- // with an mWhen that may be semi-bogus. In that case, we don't need to do
- // anything with mTimeoutInsertionPoint or anything before it, so should
- // start at the timer after mTimeoutInsertionPoint, if there is one.
+ // If insertion point is non-null, we're in the middle of firing timers and
+ // the timers we're planning to fire all come before insertion point;
+ // insertion point itself is a dummy timeout with an mWhen that may be
+ // semi-bogus. In that case, we don't need to do anything with insertion
+ // point or anything before it, so should start at the timer after insertion
+ // point, if there is one.
// Otherwise, start at the beginning of the list.
- for (Timeout* timeout = mTimeoutInsertionPoint ?
- mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
+ for (Timeout* timeout = mTimeouts.InsertionPoint() ?
+ mTimeouts.InsertionPoint()->getNext() : mTimeouts.GetFirst();
timeout; ) {
// It's important that this check be <= so that we guarantee that
// taking std::max with |now| won't make a quantity equal to
// timeout->mWhen below.
if (timeout->mWhen <= now) {
timeout = timeout->getNext();
continue;
}
@@ -682,24 +681,25 @@ TimeoutManager::ResetTimersForThrottleRe
}
void
TimeoutManager::ClearAllTimeouts()
{
Timeout* timeout;
Timeout* nextTimeout;
- for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
+ for (timeout = mTimeouts.GetFirst(); timeout; timeout = nextTimeout) {
/* If RunTimeout() is higher up on the stack for this
window, e.g. as a result of document.write from a timeout,
then we need to reset the list insertion point for
newly-created timeouts in case the user adds a timeout,
before we pop the stack back to RunTimeout. */
- if (mRunningTimeout == timeout)
- mTimeoutInsertionPoint = nullptr;
+ if (mRunningTimeout == timeout) {
+ mTimeouts.SetInsertionPoint(nullptr);
+ }
nextTimeout = timeout->getNext();
if (timeout->mTimer) {
timeout->mTimer->Cancel();
timeout->mTimer = nullptr;
// Drop the count since the timer isn't going to hold on
@@ -711,42 +711,41 @@ TimeoutManager::ClearAllTimeouts()
// cleared and taken out of the list of timeouts
timeout->mCleared = true;
// Drop the count since we're removing it from the list.
timeout->Release();
}
// Clear out our list
- mTimeouts.clear();
+ mTimeouts.Clear();
}
void
TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout)
{
- // Start at mLastTimeout and go backwards. Don't go further than
- // mTimeoutInsertionPoint, though. This optimizes for the common case of
- // insertion at the end.
+ // Start at mLastTimeout and go backwards. Don't go further than insertion
+ // point, though. This optimizes for the common case of insertion at the end.
Timeout* prevSibling;
- for (prevSibling = mTimeouts.getLast();
- prevSibling && prevSibling != mTimeoutInsertionPoint &&
+ for (prevSibling = mTimeouts.GetLast();
+ prevSibling && prevSibling != mTimeouts.InsertionPoint() &&
// This condition needs to match the one in SetTimeoutOrInterval that
// determines whether to set mWhen or mTimeRemaining.
(mWindow.IsFrozen() ?
prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
prevSibling->mWhen > aTimeout->mWhen);
prevSibling = prevSibling->getPrevious()) {
/* Do nothing; just searching */
}
// Now link in aTimeout after prevSibling.
if (prevSibling) {
prevSibling->setNext(aTimeout);
} else {
- mTimeouts.insertFront(aTimeout);
+ mTimeouts.InsertFront(aTimeout);
}
aTimeout->mFiringDepth = 0;
// Increment the timeout's reference count since it's now held on to
// by the list
aTimeout->AddRef();
}
@@ -770,29 +769,29 @@ TimeoutManager::EndRunningTimeout(Timeou
--gRunningTimeoutDepth;
mRunningTimeout = aTimeout;
}
void
TimeoutManager::UnmarkGrayTimers()
{
- for (Timeout* timeout = mTimeouts.getFirst();
+ for (Timeout* timeout = mTimeouts.GetFirst();
timeout;
timeout = timeout->getNext()) {
if (timeout->mScriptHandler) {
timeout->mScriptHandler->MarkForCC();
}
}
}
void
TimeoutManager::Suspend()
{
- for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+ for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
// Leave the timers with the current time remaining. This will
// cause the timers to potentially fire when the window is
// Resume()'d. Time effectively passes while suspended.
// Drop the XPCOM timer; we'll reschedule when restoring the state.
if (t->mTimer) {
t->mTimer->Cancel();
t->mTimer = nullptr;
@@ -805,17 +804,17 @@ TimeoutManager::Suspend()
}
void
TimeoutManager::Resume()
{
TimeStamp now = TimeStamp::Now();
DebugOnly<bool> _seenDummyTimeout = false;
- for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+ for (Timeout* t = mTimeouts.GetFirst(); t; t = t->getNext()) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = true;
continue;
}
@@ -851,17 +850,17 @@ TimeoutManager::Resume()
t->AddRef();
}
}
void
TimeoutManager::Freeze()
{
TimeStamp now = TimeStamp::Now();
- for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+ for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
// Save the current remaining time for this timeout. We will
// re-apply it when the window is Thaw()'d. This effectively
// shifts timers to the right as if time does not pass while
// the window is frozen.
if (t->mWhen > now) {
t->mTimeRemaining = t->mWhen - now;
} else {
t->mTimeRemaining = TimeDuration(0);
@@ -874,17 +873,17 @@ TimeoutManager::Freeze()
}
void
TimeoutManager::Thaw()
{
TimeStamp now = TimeStamp::Now();
DebugOnly<bool> _seenDummyTimeout = false;
- for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+ for (Timeout *t = mTimeouts.GetFirst(); t; t = t->getNext()) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = true;
continue;
}
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -23,17 +23,17 @@ public:
TimeoutManager(const TimeoutManager& rhs) = delete;
void operator=(const TimeoutManager& rhs) = delete;
bool IsRunningTimeout() const { return mTimeoutFiringDepth > 0; }
static uint32_t GetNestingLevel() { return sNestingLevel; }
static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; }
- bool HasTimeouts() const { return !mTimeouts.isEmpty(); }
+ bool HasTimeouts() const { return !mTimeouts.IsEmpty(); }
nsresult SetTimeout(nsITimeoutHandler* aHandler,
int32_t interval, bool aIsInterval,
mozilla::dom::Timeout::Reason aReason,
int32_t* aReturn);
void ClearTimeout(int32_t aTimerId,
mozilla::dom::Timeout::Reason aReason);
@@ -82,39 +82,67 @@ public:
// Initialize TimeoutManager before the first time it is accessed.
static void Initialize();
// Run some code for each Timeout in our list.
template <class Callable>
void ForEachTimeout(Callable c)
{
- for (Timeout* timeout = mTimeouts.getFirst();
+ for (Timeout* timeout = mTimeouts.GetFirst();
timeout;
timeout = timeout->getNext()) {
c(timeout);
}
}
private:
nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS);
private:
+ typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList;
+ struct Timeouts {
+ Timeouts()
+ : mTimeoutInsertionPoint(nullptr)
+ {
+ }
+
+ const Timeout* GetFirst() const { return mTimeoutList.getFirst(); }
+ Timeout* GetFirst() { return mTimeoutList.getFirst(); }
+ const Timeout* GetLast() const { return mTimeoutList.getLast(); }
+ Timeout* GetLast() { return mTimeoutList.getLast(); }
+ bool IsEmpty() const { return mTimeoutList.isEmpty(); }
+ void InsertFront(Timeout* aTimeout) { mTimeoutList.insertFront(aTimeout); }
+ void Clear() { mTimeoutList.clear(); }
+
+ void SetInsertionPoint(Timeout* aTimeout)
+ {
+ mTimeoutInsertionPoint = aTimeout;
+ }
+ Timeout* InsertionPoint()
+ {
+ return mTimeoutInsertionPoint;
+ }
+
+ private:
+ // mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is
+ // non-null. In that case, the dummy timeout pointed to by
+ // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
+ // that come after it.
+ TimeoutList mTimeoutList;
+ // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
+ // This is a dummy timeout at the moment; if that ever changes, the logic in
+ // ResetTimersForThrottleReduction needs to change.
+ mozilla::dom::Timeout* mTimeoutInsertionPoint;
+ };
+
// Each nsGlobalWindow object has a TimeoutManager member. This reference
// points to that holder object.
nsGlobalWindow& mWindow;
- // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is
- // non-null. In that case, the dummy timeout pointed to by
- // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
- // that come after it.
- mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
- // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
- // This is a dummy timeout at the moment; if that ever changes, the logic in
- // ResetTimersForThrottleReduction needs to change.
- mozilla::dom::Timeout* mTimeoutInsertionPoint;
+ Timeouts mTimeouts;
uint32_t mTimeoutIdCounter;
uint32_t mTimeoutFiringDepth;
mozilla::dom::Timeout* mRunningTimeout;
// The current idle request callback timeout handle
uint32_t mIdleCallbackTimeoutCounter;
int32_t mBackPressureDelayMS;