Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#ifndef mozilla_dom_OrderedTimeoutIterator_h__
|
|
|
|
#define mozilla_dom_OrderedTimeoutIterator_h__
|
|
|
|
|
|
|
|
#include "mozilla/RefPtr.h"
|
|
|
|
#include "mozilla/dom/Timeout.h"
|
|
|
|
#include "mozilla/dom/TimeoutManager.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
// This class implements and iterator which iterates the normal and tracking
|
|
|
|
// timeouts lists simultaneously in the mWhen order.
|
|
|
|
class MOZ_STACK_CLASS OrderedTimeoutIterator final {
|
|
|
|
public:
|
|
|
|
typedef TimeoutManager::Timeouts Timeouts;
|
|
|
|
typedef Timeouts::TimeoutList TimeoutList;
|
|
|
|
|
|
|
|
OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
|
2017-06-08 15:51:59 +03:00
|
|
|
Timeouts& aTrackingTimeouts)
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
: mNormalTimeouts(aNormalTimeouts.mTimeoutList),
|
|
|
|
mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
|
|
|
|
mNormalIter(mNormalTimeouts.getFirst()),
|
|
|
|
mTrackingIter(mTrackingTimeouts.getFirst()),
|
|
|
|
mKind(Kind::None),
|
|
|
|
mUpdateIteratorCalled(true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the current timeout and move to the next one.
|
|
|
|
// Unless this is the first time calling Next(), you must call
|
|
|
|
// UpdateIterator() before calling this method.
|
|
|
|
Timeout* Next()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mUpdateIteratorCalled);
|
2017-06-08 15:51:59 +03:00
|
|
|
MOZ_ASSERT_IF(mNormalIter, mNormalIter->isInList());
|
|
|
|
MOZ_ASSERT_IF(mTrackingIter, mTrackingIter->isInList());
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
|
|
|
|
mUpdateIteratorCalled = false;
|
|
|
|
mKind = Kind::None;
|
|
|
|
Timeout* timeout = nullptr;
|
2017-06-08 15:51:59 +03:00
|
|
|
if (!mNormalIter) {
|
|
|
|
if (!mTrackingIter) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
// We have reached the end of both lists. Bail out!
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
// We have reached the end of the normal timeout list, select the next
|
|
|
|
// tracking timeout.
|
|
|
|
timeout = mTrackingIter;
|
|
|
|
mKind = Kind::Tracking;
|
|
|
|
}
|
2017-06-08 15:51:59 +03:00
|
|
|
} else if (!mTrackingIter) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
// We have reached the end of the tracking timeout list, select the next
|
|
|
|
// normal timeout.
|
|
|
|
timeout = mNormalIter;
|
|
|
|
mKind = Kind::Normal;
|
|
|
|
} else {
|
|
|
|
// If we have a normal and a tracking timer, return the one with the
|
|
|
|
// smaller mWhen (and prefer the timeout with a lower ID in case they are
|
|
|
|
// equal.) Otherwise, return whichever iterator has an item left,
|
|
|
|
// preferring a non-tracking timeout again. Note that in practice, even
|
|
|
|
// if a web page calls setTimeout() twice in a row, it should get
|
|
|
|
// different mWhen values, so in practice we shouldn't fall back to
|
|
|
|
// comparing timeout IDs.
|
|
|
|
if (mNormalIter && mTrackingIter &&
|
2017-01-10 19:08:18 +03:00
|
|
|
(mTrackingIter->When() < mNormalIter->When() ||
|
|
|
|
(mTrackingIter->When() == mNormalIter->When() &&
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
|
|
|
|
timeout = mTrackingIter;
|
|
|
|
mKind = Kind::Tracking;
|
2017-06-08 15:51:59 +03:00
|
|
|
} else if (mNormalIter) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
timeout = mNormalIter;
|
|
|
|
mKind = Kind::Normal;
|
2017-06-08 15:51:59 +03:00
|
|
|
} else if (mTrackingIter) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
timeout = mTrackingIter;
|
|
|
|
mKind = Kind::Tracking;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!timeout) {
|
|
|
|
// We didn't find any suitable iterator. This can happen for example
|
|
|
|
// when getNext() in UpdateIterator() returns nullptr and then Next()
|
|
|
|
// gets called. Bail out!
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mKind != Kind::None);
|
|
|
|
|
|
|
|
// Record the current timeout we just found.
|
|
|
|
mCurrent = timeout;
|
|
|
|
MOZ_ASSERT(mCurrent);
|
|
|
|
|
|
|
|
return mCurrent;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare the iterator for the next call to Next().
|
|
|
|
// This method can be called as many times as needed. Calling this more than
|
|
|
|
// once is helpful in cases where we expect the timeouts list has been
|
|
|
|
// modified before we got a chance to call Next().
|
|
|
|
void UpdateIterator()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mKind != Kind::None);
|
|
|
|
// Update the winning iterator to point to the next element. Also check to
|
|
|
|
// see if the other iterator is still valid, otherwise reset it to the
|
|
|
|
// beginning of the list. This is needed in case a timeout handler removes
|
|
|
|
// the timeout pointed to from one of our iterators.
|
|
|
|
if (mKind == Kind::Normal) {
|
|
|
|
mNormalIter = mCurrent->getNext();
|
2017-06-08 15:51:59 +03:00
|
|
|
if (mTrackingIter && !mTrackingIter->isInList()) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
mTrackingIter = mTrackingTimeouts.getFirst();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mTrackingIter = mCurrent->getNext();
|
2017-06-08 15:51:59 +03:00
|
|
|
if (mNormalIter && !mNormalIter->isInList()) {
|
Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior. Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.
The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts. The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.
In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order. Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-16 00:17:38 +03:00
|
|
|
mNormalIter = mNormalTimeouts.getFirst();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mUpdateIteratorCalled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function resets the iterator to a defunct state. It should only be
|
|
|
|
// used when we want to forcefully sever all of the strong references this
|
|
|
|
// class holds.
|
|
|
|
void Clear()
|
|
|
|
{
|
|
|
|
// Release all strong references.
|
|
|
|
mNormalIter = nullptr;
|
|
|
|
mTrackingIter = nullptr;
|
|
|
|
mCurrent = nullptr;
|
|
|
|
mKind = Kind::None;
|
|
|
|
mUpdateIteratorCalled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the previous call to Next() picked a normal timeout.
|
|
|
|
// Cannot be called before Next() has been called. Note that the result of
|
|
|
|
// this method is only affected by Next() and not UpdateIterator(), so calling
|
|
|
|
// UpdateIterator() before calling this is allowed.
|
|
|
|
bool PickedNormalIter() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mKind != Kind::None);
|
|
|
|
return mKind == Kind::Normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if the previous call to Next() picked a tracking timeout.
|
|
|
|
// Cannot be called before Next() has been called. Note that the result of
|
|
|
|
// this method is only affected by Next() and not UpdateIterator(), so calling
|
|
|
|
// UpdateIterator() before calling this is allowed.
|
|
|
|
bool PickedTrackingIter() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mKind != Kind::None);
|
|
|
|
return mKind == Kind::Tracking;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
TimeoutList& mNormalTimeouts; // The list of normal timeouts.
|
|
|
|
TimeoutList& mTrackingTimeouts; // The list of tracking timeouts.
|
|
|
|
RefPtr<Timeout> mNormalIter; // The iterator over the normal timeout list.
|
|
|
|
RefPtr<Timeout> mTrackingIter; // The iterator over the tracking timeout list.
|
|
|
|
RefPtr<Timeout> mCurrent; // The current timeout that Next() just found.
|
|
|
|
enum class Kind { Normal, Tracking, None };
|
|
|
|
Kind mKind; // The kind of iterator picked the last time.
|
|
|
|
DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next().
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|