зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1363829 P6 Use the TimeoutExecutor in TimeoutManager. r=ehsan
This commit is contained in:
Родитель
e9e20536c7
Коммит
f698e87bfb
|
@ -58,7 +58,7 @@ void
|
|||
TimerCallback(nsITimer*, void* aClosure)
|
||||
{
|
||||
RefPtr<Timeout> timeout = (Timeout*)aClosure;
|
||||
timeout->mWindow->AsInner()->TimeoutManager().RunTimeout(timeout->When());
|
||||
timeout->mWindow->AsInner()->TimeoutManager().RunTimeout(TimeStamp::Now(), timeout->When());
|
||||
timeout->mClosureSelfRef = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ TimeoutExecutor::MaybeExecute()
|
|||
|
||||
Cancel();
|
||||
|
||||
mOwner->RunTimeout(deadline);
|
||||
mOwner->RunTimeout(now, deadline);
|
||||
}
|
||||
|
||||
TimeoutExecutor::TimeoutExecutor(TimeoutManager* aOwner)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsITimeoutHandler.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "OrderedTimeoutIterator.h"
|
||||
#include "TimeoutExecutor.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -301,6 +302,7 @@ CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth)
|
|||
|
||||
TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
|
||||
: mWindow(aWindow),
|
||||
mExecutor(new TimeoutExecutor(this)),
|
||||
mTimeoutIdCounter(1),
|
||||
mNextFiringId(InvalidFiringId + 1),
|
||||
mRunningTimeout(nullptr),
|
||||
|
@ -320,6 +322,8 @@ TimeoutManager::~TimeoutManager()
|
|||
MOZ_DIAGNOSTIC_ASSERT(mWindow.AsInner()->InnerObjectsFreed());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mThrottleTrackingTimeoutsTimer);
|
||||
|
||||
mExecutor->Shutdown();
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("TimeoutManager %p destroyed\n", this));
|
||||
}
|
||||
|
@ -474,14 +478,7 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
if (!mWindow.IsSuspended()) {
|
||||
MOZ_ASSERT(!timeout->When().IsNull());
|
||||
|
||||
nsresult rv;
|
||||
timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = timeout->InitTimer(mWindow.EventTargetFor(TaskCategory::Timer),
|
||||
realInterval);
|
||||
nsresult rv = mExecutor->MaybeSchedule(timeout->When());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -545,6 +542,8 @@ TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
|
|||
{
|
||||
uint32_t timerId = (uint32_t)aTimerId;
|
||||
|
||||
bool firstTimeout = true;
|
||||
|
||||
ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) {
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Clear%s(TimeoutManager=%p, timeout=%p, aTimerId=%u, ID=%u, tracking=%d)\n", aTimeout->mIsInterval ? "Interval" : "Timeout",
|
||||
|
@ -560,18 +559,38 @@ TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
|
|||
}
|
||||
else {
|
||||
/* Delete the aTimeout from the pending aTimeout list */
|
||||
aTimeout->MaybeCancelTimer();
|
||||
aTimeout->remove();
|
||||
}
|
||||
return true; // abort!
|
||||
}
|
||||
|
||||
firstTimeout = false;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!firstTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the first timeout was cancelled we need to stop the executor and
|
||||
// restart at the next soonest deadline.
|
||||
mExecutor->Cancel();
|
||||
|
||||
OrderedTimeoutIterator iter(mNormalTimeouts,
|
||||
mTrackingTimeouts,
|
||||
nullptr,
|
||||
nullptr);
|
||||
Timeout* nextTimeout = iter.Next();
|
||||
if (nextTimeout) {
|
||||
MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(nextTimeout->When()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
||||
TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadline)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!aNow.IsNull());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!aTargetDeadline.IsNull());
|
||||
|
||||
if (mWindow.IsSuspended()) {
|
||||
|
@ -595,7 +614,8 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
|
||||
// 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();
|
||||
TimeStamp now(aNow);
|
||||
TimeStamp start = now;
|
||||
|
||||
Timeout* last_expired_normal_timeout = nullptr;
|
||||
Timeout* last_expired_tracking_timeout = nullptr;
|
||||
|
@ -618,7 +638,6 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
|
||||
// A native timer has gone off. See which of our timeouts need
|
||||
// servicing
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
TimeStamp deadline;
|
||||
|
||||
if (aTargetDeadline > now) {
|
||||
|
@ -632,6 +651,8 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
deadline = now;
|
||||
}
|
||||
|
||||
TimeStamp nextDeadline;
|
||||
|
||||
// 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 aTargetDeadline
|
||||
|
@ -651,6 +672,9 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
while (true) {
|
||||
Timeout* timeout = expiredIter.Next();
|
||||
if (!timeout || timeout->When() > deadline) {
|
||||
if (timeout) {
|
||||
nextDeadline = timeout->When();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -669,8 +693,10 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
|
||||
// Run only a limited number of timers based on the configured maximum.
|
||||
if (numTimersToRun % kNumTimersPerInitialElapsedCheck == 0) {
|
||||
TimeDuration elapsed(TimeStamp::Now() - start);
|
||||
now = TimeStamp::Now();
|
||||
TimeDuration elapsed(now - start);
|
||||
if (elapsed >= initalTimeLimit) {
|
||||
nextDeadline = timeout->When();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -680,6 +706,17 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
}
|
||||
}
|
||||
|
||||
now = TimeStamp::Now();
|
||||
|
||||
// Wherever we stopped in the timer list, schedule the executor to
|
||||
// run for the next unexpired deadline. Note, this *must* be done
|
||||
// before we start executing any content script handlers. If one
|
||||
// of them spins the event loop the executor must already be scheduled
|
||||
// in order for timeouts to fire properly.
|
||||
if (!nextDeadline.IsNull()) {
|
||||
MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(nextDeadline));
|
||||
}
|
||||
|
||||
// Maybe the timeout that the event was fired for has been deleted
|
||||
// and there are no others timeouts with deadlines that make them
|
||||
// eligible for execution yet. Go away.
|
||||
|
@ -798,6 +835,8 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
return;
|
||||
}
|
||||
|
||||
now = TimeStamp::Now();
|
||||
|
||||
// If we have a regular interval timer, we re-schedule the
|
||||
// timeout, accounting for clock drift.
|
||||
bool needsReinsertion = RescheduleTimeout(timeout, now);
|
||||
|
@ -824,8 +863,14 @@ TimeoutManager::RunTimeout(const TimeStamp& aTargetDeadline)
|
|||
|
||||
// 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.
|
||||
TimeDuration elapsed = TimeStamp::Now() - start;
|
||||
TimeDuration elapsed = now - start;
|
||||
if (elapsed >= totalTimeLimit) {
|
||||
// We ran out of time. Make sure to schedule the executor to
|
||||
// run immediately for the next timer, if it exists.
|
||||
RefPtr<Timeout> timeout = runIter.Next();
|
||||
if (timeout) {
|
||||
MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(timeout->When()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -960,10 +1005,6 @@ bool
|
|||
TimeoutManager::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now)
|
||||
{
|
||||
if (!aTimeout->mIsInterval) {
|
||||
// The timeout still has an OS timer, and it's not an interval,
|
||||
// that means that the OS timer could still fire; cancel the OS
|
||||
// timer and release its reference to the timeout.
|
||||
aTimeout->MaybeCancelTimer();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -988,29 +1029,12 @@ TimeoutManager::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now)
|
|||
|
||||
aTimeout->SetWhenOrTimeRemaining(currentNow, delay);
|
||||
|
||||
if (!aTimeout->mTimer) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWindow.IsFrozen() || mWindow.IsSuspended());
|
||||
if (mWindow.IsSuspended()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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(mWindow.EventTargetFor(TaskCategory::Timer),
|
||||
delay.ToMilliseconds());
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Error initializing timer for DOM timeout!");
|
||||
|
||||
// We failed to initialize the new OS timer, this timer does
|
||||
// us no good here so we just cancel it (just in case) and
|
||||
// null out the pointer to the OS timer, this will release the
|
||||
// OS timer. As we continue executing the code below we'll end
|
||||
// up deleting the timeout since it's not an interval timeout
|
||||
// any more (since timeout->mTimer == nullptr).
|
||||
aTimeout->MaybeCancelTimer();
|
||||
|
||||
return false;
|
||||
}
|
||||
nsresult rv = mExecutor->MaybeSchedule(aTimeout->When());
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1033,26 +1057,32 @@ TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS
|
|||
Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
||||
: Timeouts::SortBy::TimeWhen;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> queue = mWindow.EventTargetFor(TaskCategory::Timer);
|
||||
nsresult rv = mNormalTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
|
||||
*this,
|
||||
sortBy,
|
||||
queue);
|
||||
sortBy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mTrackingTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
|
||||
*this,
|
||||
sortBy,
|
||||
queue);
|
||||
sortBy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
OrderedTimeoutIterator iter(mNormalTimeouts,
|
||||
mTrackingTimeouts,
|
||||
nullptr,
|
||||
nullptr);
|
||||
Timeout* firstTimeout = iter.Next();
|
||||
if (firstTimeout) {
|
||||
rv = mExecutor->MaybeSchedule(firstTimeout->When());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
|
||||
const TimeoutManager& aTimeoutManager,
|
||||
SortBy aSortBy,
|
||||
nsIEventTarget* aQueue)
|
||||
SortBy aSortBy)
|
||||
{
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
|
@ -1131,13 +1161,6 @@ TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrot
|
|||
timeout->mFiringId = firingId;
|
||||
}
|
||||
|
||||
nsresult rv = timeout->InitTimer(aQueue, delay.ToMilliseconds());
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Error resetting non background timer for DOM timeout!");
|
||||
return rv;
|
||||
}
|
||||
|
||||
timeout = nextTimeout;
|
||||
} else {
|
||||
timeout = timeout->getNext();
|
||||
|
@ -1160,6 +1183,8 @@ TimeoutManager::ClearAllTimeouts()
|
|||
mThrottleTrackingTimeoutsTimer = nullptr;
|
||||
}
|
||||
|
||||
mExecutor->Cancel();
|
||||
|
||||
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
||||
/* If RunTimeout() is higher up on the stack for this
|
||||
window, e.g. as a result of document.write from a timeout,
|
||||
|
@ -1170,8 +1195,6 @@ TimeoutManager::ClearAllTimeouts()
|
|||
seenRunningTimeout = true;
|
||||
}
|
||||
|
||||
aTimeout->MaybeCancelTimer();
|
||||
|
||||
// Set timeout->mCleared to true to indicate that the timeout was
|
||||
// cleared and taken out of the list of timeouts
|
||||
aTimeout->mCleared = true;
|
||||
|
@ -1279,14 +1302,7 @@ TimeoutManager::Suspend()
|
|||
mThrottleTrackingTimeoutsTimer = nullptr;
|
||||
}
|
||||
|
||||
ForEachUnorderedTimeout([](Timeout* aTimeout) {
|
||||
// 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.
|
||||
aTimeout->MaybeCancelTimer();
|
||||
});
|
||||
mExecutor->Cancel();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1305,6 +1321,8 @@ TimeoutManager::Resume()
|
|||
TimeStamp now = TimeStamp::Now();
|
||||
DebugOnly<bool> _seenDummyTimeout = false;
|
||||
|
||||
TimeStamp nextWakeUp;
|
||||
|
||||
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
||||
// 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
|
||||
|
@ -1315,34 +1333,25 @@ TimeoutManager::Resume()
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!aTimeout->mTimer);
|
||||
|
||||
// The timeout When() is set to the absolute time when the timer should
|
||||
// fire. Recalculate the delay from now until that deadline. If the
|
||||
// the deadline has already passed or falls within our minimum delay
|
||||
// deadline, then clamp the resulting value to the minimum delay. The
|
||||
// When() will remain at its absolute time, but we won'aTimeout fire the OS
|
||||
// timer until our calculated delay has passed.
|
||||
// deadline, then clamp the resulting value to the minimum delay.
|
||||
int32_t remaining = 0;
|
||||
if (aTimeout->When() > now) {
|
||||
remaining = static_cast<int32_t>((aTimeout->When() - now).ToMilliseconds());
|
||||
}
|
||||
uint32_t delay = std::max(remaining, DOMMinTimeoutValue(aTimeout->mIsTracking));
|
||||
aTimeout->SetWhenOrTimeRemaining(now, TimeDuration::FromMilliseconds(delay));
|
||||
|
||||
aTimeout->mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!aTimeout->mTimer) {
|
||||
aTimeout->remove();
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = aTimeout->InitTimer(mWindow.EventTargetFor(TaskCategory::Timer),
|
||||
delay);
|
||||
if (NS_FAILED(rv)) {
|
||||
aTimeout->mTimer = nullptr;
|
||||
aTimeout->remove();
|
||||
return;
|
||||
if (nextWakeUp.IsNull() || aTimeout->When() < nextWakeUp) {
|
||||
nextWakeUp = aTimeout->When();
|
||||
}
|
||||
});
|
||||
|
||||
if (!nextWakeUp.IsNull()) {
|
||||
MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(nextWakeUp));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1371,10 +1380,6 @@ TimeoutManager::Freeze()
|
|||
}
|
||||
aTimeout->SetWhenOrTimeRemaining(now, delta);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aTimeout->TimeRemaining() == delta);
|
||||
|
||||
// Since we are suspended there should be no OS timer set for
|
||||
// this timeout entry.
|
||||
MOZ_ASSERT(!aTimeout->mTimer);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1400,8 +1405,6 @@ TimeoutManager::Thaw()
|
|||
// Set When() back to the time when the timer is supposed to fire.
|
||||
aTimeout->SetWhenOrTimeRemaining(now, aTimeout->TimeRemaining());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!aTimeout->When().IsNull());
|
||||
|
||||
MOZ_ASSERT(!aTimeout->mTimer);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
|
||||
class OrderedTimeoutIterator;
|
||||
class TimeoutExecutor;
|
||||
|
||||
// This class manages the timeouts in a Window's setTimeout/setInterval pool.
|
||||
class TimeoutManager final
|
||||
|
@ -47,7 +48,7 @@ public:
|
|||
mozilla::dom::Timeout::Reason aReason);
|
||||
|
||||
// The timeout implementation functions.
|
||||
void RunTimeout(const TimeStamp& aTargetDeadline);
|
||||
void RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadline);
|
||||
// Return true if |aTimeout| needs to be reinserted into the timeout list.
|
||||
bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, const TimeStamp& now);
|
||||
|
||||
|
@ -155,8 +156,7 @@ private:
|
|||
void Insert(mozilla::dom::Timeout* aTimeout, SortBy aSortBy);
|
||||
nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
|
||||
const TimeoutManager& aTimeoutManager,
|
||||
SortBy aSortBy,
|
||||
nsIEventTarget* aQueue);
|
||||
SortBy aSortBy);
|
||||
|
||||
const Timeout* GetFirst() const { return mTimeoutList.getFirst(); }
|
||||
Timeout* GetFirst() { return mTimeoutList.getFirst(); }
|
||||
|
@ -220,6 +220,10 @@ private:
|
|||
// Each nsGlobalWindow object has a TimeoutManager member. This reference
|
||||
// points to that holder object.
|
||||
nsGlobalWindow& mWindow;
|
||||
// The executor is specific to the nsGlobalWindow/TimeoutManager, but it
|
||||
// can live past the destruction of the window if its scheduled. Therefore
|
||||
// it must be a separate ref-counted object.
|
||||
RefPtr<TimeoutExecutor> mExecutor;
|
||||
// The list of timeouts coming from non-tracking scripts.
|
||||
Timeouts mNormalTimeouts;
|
||||
// The list of timeouts coming from scripts on the tracking protection list.
|
||||
|
|
Загрузка…
Ссылка в новой задаче