2016-12-03 01:22:48 +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/. */
|
|
|
|
|
|
|
|
#include "TimeoutManager.h"
|
|
|
|
#include "nsGlobalWindow.h"
|
2017-01-10 06:16:31 +03:00
|
|
|
#include "mozilla/Logging.h"
|
2017-05-02 14:23:00 +03:00
|
|
|
#include "mozilla/Telemetry.h"
|
2016-12-03 01:22:48 +03:00
|
|
|
#include "mozilla/ThrottledEventQueue.h"
|
|
|
|
#include "mozilla/TimeStamp.h"
|
|
|
|
#include "nsITimeoutHandler.h"
|
|
|
|
#include "mozilla/dom/TabGroup.h"
|
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
|
|
|
#include "OrderedTimeoutIterator.h"
|
2017-06-01 03:13:19 +03:00
|
|
|
#include "TimeoutExecutor.h"
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2016-12-14 03:06:21 +03:00
|
|
|
using namespace mozilla;
|
2016-12-03 01:22:48 +03:00
|
|
|
using namespace mozilla::dom;
|
|
|
|
|
2017-01-10 06:16:31 +03:00
|
|
|
static LazyLogModule gLog("Timeout");
|
|
|
|
|
2017-05-02 14:23:00 +03:00
|
|
|
// Time between sampling timeout execution time.
|
|
|
|
const uint32_t kTelemetryPeriodMS = 1000;
|
|
|
|
|
|
|
|
class TimeoutTelemetry
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static TimeoutTelemetry& Get();
|
|
|
|
TimeoutTelemetry() : mLastCollection(TimeStamp::Now()) {}
|
|
|
|
|
|
|
|
void StartRecording(TimeStamp aNow);
|
|
|
|
void StopRecording();
|
|
|
|
void RecordExecution(TimeStamp aNow, Timeout* aTimeout, bool aIsBackground);
|
|
|
|
void MaybeCollectTelemetry(TimeStamp aNow);
|
|
|
|
private:
|
|
|
|
struct TelemetryData
|
|
|
|
{
|
|
|
|
TimeDuration mForegroundTracking;
|
|
|
|
TimeDuration mForegroundNonTracking;
|
|
|
|
TimeDuration mBackgroundTracking;
|
|
|
|
TimeDuration mBackgroundNonTracking;
|
|
|
|
};
|
|
|
|
|
|
|
|
void Accumulate(Telemetry::HistogramID aId, TimeDuration aSample);
|
|
|
|
|
|
|
|
TelemetryData mTelemetryData;
|
|
|
|
TimeStamp mStart;
|
|
|
|
TimeStamp mLastCollection;
|
|
|
|
};
|
|
|
|
|
|
|
|
static TimeoutTelemetry gTimeoutTelemetry;
|
|
|
|
|
|
|
|
/* static */ TimeoutTelemetry&
|
|
|
|
TimeoutTelemetry::Get()
|
|
|
|
{
|
|
|
|
return gTimeoutTelemetry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutTelemetry::StartRecording(TimeStamp aNow)
|
|
|
|
{
|
|
|
|
mStart = aNow;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutTelemetry::StopRecording()
|
|
|
|
{
|
|
|
|
mStart = TimeStamp();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutTelemetry::RecordExecution(TimeStamp aNow,
|
|
|
|
Timeout* aTimeout,
|
|
|
|
bool aIsBackground)
|
|
|
|
{
|
|
|
|
if (!mStart) {
|
|
|
|
// If we've started a sync operation mStart might be null, in
|
|
|
|
// which case we should not record this piece of execution.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TimeDuration duration = aNow - mStart;
|
|
|
|
|
|
|
|
if (aIsBackground) {
|
|
|
|
if (aTimeout->mIsTracking) {
|
|
|
|
mTelemetryData.mBackgroundTracking += duration;
|
|
|
|
} else {
|
|
|
|
mTelemetryData.mBackgroundNonTracking += duration;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (aTimeout->mIsTracking) {
|
|
|
|
mTelemetryData.mForegroundTracking += duration;
|
|
|
|
} else {
|
|
|
|
mTelemetryData.mForegroundNonTracking += duration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutTelemetry::Accumulate(Telemetry::HistogramID aId, TimeDuration aSample)
|
|
|
|
{
|
|
|
|
uint32_t sample = std::round(aSample.ToMilliseconds());
|
|
|
|
if (sample) {
|
|
|
|
Telemetry::Accumulate(aId, sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutTelemetry::MaybeCollectTelemetry(TimeStamp aNow)
|
|
|
|
{
|
|
|
|
if ((aNow - mLastCollection).ToMilliseconds() < kTelemetryPeriodMS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Accumulate(Telemetry::TIMEOUT_EXECUTION_FG_TRACKING_MS,
|
|
|
|
mTelemetryData.mForegroundTracking);
|
|
|
|
Accumulate(Telemetry::TIMEOUT_EXECUTION_FG_MS,
|
|
|
|
mTelemetryData.mForegroundNonTracking);
|
|
|
|
Accumulate(Telemetry::TIMEOUT_EXECUTION_BG_TRACKING_MS,
|
|
|
|
mTelemetryData.mBackgroundTracking);
|
|
|
|
Accumulate(Telemetry::TIMEOUT_EXECUTION_BG_MS,
|
|
|
|
mTelemetryData.mBackgroundNonTracking);
|
|
|
|
|
|
|
|
mTelemetryData = TelemetryData();
|
|
|
|
mLastCollection = aNow;
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
static int32_t gRunningTimeoutDepth = 0;
|
|
|
|
|
|
|
|
// The default shortest interval/timeout we permit
|
|
|
|
#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
|
|
|
|
#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
|
2016-12-22 06:56:45 +03:00
|
|
|
#define DEFAULT_MIN_TRACKING_TIMEOUT_VALUE 4 // 4ms
|
|
|
|
#define DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
|
|
|
|
static int32_t gMinTimeoutValue = 0;
|
|
|
|
static int32_t gMinBackgroundTimeoutValue = 0;
|
|
|
|
static int32_t gMinTrackingTimeoutValue = 0;
|
|
|
|
static int32_t gMinTrackingBackgroundTimeoutValue = 0;
|
2017-02-13 23:58:39 +03:00
|
|
|
static int32_t gTrackingTimeoutThrottlingDelay = 0;
|
2017-01-10 06:16:31 +03:00
|
|
|
static bool gAnnotateTrackingChannels = false;
|
2017-05-02 14:23:00 +03:00
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// static
|
|
|
|
const uint32_t TimeoutManager::InvalidFiringId = 0;
|
|
|
|
|
2017-05-02 14:23:00 +03:00
|
|
|
bool
|
|
|
|
TimeoutManager::IsBackground() const
|
|
|
|
{
|
|
|
|
return !mWindow.AsInner()->IsPlayingAudio() && mWindow.IsBackgroundInternal();
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
uint32_t
|
|
|
|
TimeoutManager::CreateFiringId()
|
|
|
|
{
|
|
|
|
uint32_t id = mNextFiringId;
|
|
|
|
mNextFiringId += 1;
|
|
|
|
if (mNextFiringId == InvalidFiringId) {
|
|
|
|
mNextFiringId += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mFiringIdStack.AppendElement(id);
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::DestroyFiringId(uint32_t aFiringId)
|
|
|
|
{
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mFiringIdStack.IsEmpty());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mFiringIdStack.LastElement() == aFiringId);
|
|
|
|
mFiringIdStack.RemoveElementAt(mFiringIdStack.Length() - 1);
|
|
|
|
}
|
|
|
|
|
2017-06-05 22:42:33 +03:00
|
|
|
bool
|
|
|
|
TimeoutManager::IsValidFiringId(uint32_t aFiringId) const
|
|
|
|
{
|
|
|
|
return !IsInvalidFiringId(aFiringId);
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
bool
|
|
|
|
TimeoutManager::IsInvalidFiringId(uint32_t aFiringId) const
|
|
|
|
{
|
|
|
|
// Check the most common ways to invalidate a firing id first.
|
|
|
|
// These should be quite fast.
|
|
|
|
if (aFiringId == InvalidFiringId ||
|
2017-06-05 22:42:33 +03:00
|
|
|
mFiringIdStack.IsEmpty()) {
|
2017-05-19 23:45:55 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-05 22:42:33 +03:00
|
|
|
if (mFiringIdStack.Length() == 1) {
|
|
|
|
return mFiringIdStack[0] != aFiringId;
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// Next do a range check on the first and last items in the stack
|
|
|
|
// of active firing ids. This is a bit slower.
|
|
|
|
uint32_t low = mFiringIdStack[0];
|
|
|
|
uint32_t high = mFiringIdStack.LastElement();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(low != high);
|
|
|
|
if (low > high) {
|
|
|
|
// If the first element is bigger than the last element in the
|
|
|
|
// stack, that means mNextFiringId wrapped around to zero at
|
|
|
|
// some point.
|
|
|
|
Swap(low, high);
|
|
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(low < high);
|
|
|
|
|
|
|
|
if (aFiringId < low || aFiringId > high) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, fall back to verifying the firing id is not anywhere
|
|
|
|
// in the stack. This could be slow for a large stack, but that
|
|
|
|
// should be rare. It can only happen with deeply nested event
|
|
|
|
// loop spinning. For example, a page that does a lot of timers
|
|
|
|
// and a lot of sync XHRs within those timers could be slow here.
|
|
|
|
return !mFiringIdStack.Contains(aFiringId);
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
int32_t
|
2016-12-22 06:40:10 +03:00
|
|
|
TimeoutManager::DOMMinTimeoutValue(bool aIsTracking) const {
|
2017-02-09 04:03:22 +03:00
|
|
|
// Don't use the background timeout value when the tab is playing audio.
|
|
|
|
// Until bug 1336484 we only used to do this for pages that use Web Audio.
|
|
|
|
// The original behavior was implemented in bug 11811073.
|
2017-05-02 14:23:00 +03:00
|
|
|
bool isBackground = IsBackground();
|
2017-02-13 23:58:39 +03:00
|
|
|
bool throttleTracking = aIsTracking && mThrottleTrackingTimeouts;
|
|
|
|
auto minValue = throttleTracking ? (isBackground ? gMinTrackingBackgroundTimeoutValue
|
|
|
|
: gMinTrackingTimeoutValue)
|
|
|
|
: (isBackground ? gMinBackgroundTimeoutValue
|
|
|
|
: gMinTimeoutValue);
|
2017-06-01 03:13:19 +03:00
|
|
|
return minValue;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
2016-12-16 00:24:43 +03:00
|
|
|
#define TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY 0 // Consider all timeouts coming from tracking scripts as tracking
|
|
|
|
// These strategies are useful for testing.
|
|
|
|
#define ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY 1 // Consider all timeouts as normal
|
|
|
|
#define ALTERNATE_TIMEOUT_BUCKETING_STRATEGY 2 // Put every other timeout in the list of tracking timeouts
|
|
|
|
#define RANDOM_TIMEOUT_BUCKETING_STRATEGY 3 // Put timeouts into either the normal or tracking timeouts list randomly
|
|
|
|
static int32_t gTimeoutBucketingStrategy = 0;
|
|
|
|
|
2017-02-13 23:58:39 +03:00
|
|
|
#define DEFAULT_TRACKING_TIMEOUT_THROTTLING_DELAY -1 // Only positive integers cause us to introduce a delay for tracking
|
|
|
|
// timeout throttling.
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
|
|
|
|
// uses 5.
|
|
|
|
#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
|
|
|
|
|
|
|
|
// The longest interval (as PRIntervalTime) we permit, or that our
|
|
|
|
// timer code can handle, really. See DELAY_INTERVAL_LIMIT in
|
|
|
|
// nsTimerImpl.h for details.
|
|
|
|
#define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT
|
|
|
|
|
|
|
|
uint32_t TimeoutManager::sNestingLevel = 0;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// The maximum number of milliseconds to allow consecutive timer callbacks
|
|
|
|
// to run in a single event loop runnable.
|
|
|
|
#define DEFAULT_MAX_CONSECUTIVE_CALLBACKS_MILLISECONDS 4
|
|
|
|
uint32_t gMaxConsecutiveCallbacksMilliseconds;
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
|
|
|
|
: mWindow(aWindow),
|
2017-06-01 03:13:19 +03:00
|
|
|
mExecutor(new TimeoutExecutor(this)),
|
2016-12-03 01:22:48 +03:00
|
|
|
mTimeoutIdCounter(1),
|
2017-05-19 23:45:55 +03:00
|
|
|
mNextFiringId(InvalidFiringId + 1),
|
2016-12-03 01:22:48 +03:00
|
|
|
mRunningTimeout(nullptr),
|
|
|
|
mIdleCallbackTimeoutCounter(1),
|
2017-05-24 20:10:15 +03:00
|
|
|
mThrottleTrackingTimeouts(false)
|
2016-12-03 01:22:48 +03:00
|
|
|
{
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aWindow.IsInnerWindow());
|
2017-01-10 06:16:31 +03:00
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("TimeoutManager %p created, tracking bucketing %s\n",
|
|
|
|
this, gAnnotateTrackingChannels ? "enabled" : "disabled"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TimeoutManager::~TimeoutManager()
|
|
|
|
{
|
2017-05-15 14:06:28 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWindow.AsInner()->InnerObjectsFreed());
|
2017-02-13 23:58:39 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mThrottleTrackingTimeoutsTimer);
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
mExecutor->Shutdown();
|
|
|
|
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("TimeoutManager %p destroyed\n", this));
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void
|
|
|
|
TimeoutManager::Initialize()
|
|
|
|
{
|
|
|
|
Preferences::AddIntVarCache(&gMinTimeoutValue,
|
|
|
|
"dom.min_timeout_value",
|
|
|
|
DEFAULT_MIN_TIMEOUT_VALUE);
|
|
|
|
Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
|
|
|
|
"dom.min_background_timeout_value",
|
|
|
|
DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
|
2016-12-22 06:56:45 +03:00
|
|
|
Preferences::AddIntVarCache(&gMinTrackingTimeoutValue,
|
|
|
|
"dom.min_tracking_timeout_value",
|
|
|
|
DEFAULT_MIN_TRACKING_TIMEOUT_VALUE);
|
|
|
|
Preferences::AddIntVarCache(&gMinTrackingBackgroundTimeoutValue,
|
|
|
|
"dom.min_tracking_background_timeout_value",
|
2016-12-14 23:52:57 +03:00
|
|
|
DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE);
|
2016-12-16 00:24:43 +03:00
|
|
|
Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
|
|
|
|
"dom.timeout_bucketing_strategy",
|
|
|
|
TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
|
2017-02-13 23:58:39 +03:00
|
|
|
Preferences::AddIntVarCache(&gTrackingTimeoutThrottlingDelay,
|
|
|
|
"dom.timeout.tracking_throttling_delay",
|
|
|
|
DEFAULT_TRACKING_TIMEOUT_THROTTLING_DELAY);
|
2017-01-10 06:16:31 +03:00
|
|
|
Preferences::AddBoolVarCache(&gAnnotateTrackingChannels,
|
|
|
|
"privacy.trackingprotection.annotate_channels",
|
|
|
|
false);
|
2017-02-06 05:50:12 +03:00
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
Preferences::AddUintVarCache(&gMaxConsecutiveCallbacksMilliseconds,
|
|
|
|
"dom.timeout.max_consecutive_callbacks_ms",
|
|
|
|
DEFAULT_MAX_CONSECUTIVE_CALLBACKS_MILLISECONDS);
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
|
|
|
|
{
|
|
|
|
switch (aReason) {
|
|
|
|
case Timeout::Reason::eIdleCallbackTimeout:
|
|
|
|
return ++mIdleCallbackTimeoutCounter;
|
|
|
|
case Timeout::Reason::eTimeoutOrInterval:
|
|
|
|
default:
|
|
|
|
return ++mTimeoutIdCounter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
bool
|
|
|
|
TimeoutManager::IsRunningTimeout() const
|
|
|
|
{
|
|
|
|
return mRunningTimeout;
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
nsresult
|
|
|
|
TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|
|
|
int32_t interval, bool aIsInterval,
|
|
|
|
Timeout::Reason aReason, int32_t* aReturn)
|
|
|
|
{
|
|
|
|
// If we don't have a document (we could have been unloaded since
|
|
|
|
// the call to setTimeout was made), do nothing.
|
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
|
|
|
nsCOMPtr<nsIDocument> doc = mWindow.GetExtantDoc();
|
|
|
|
if (!doc) {
|
2016-12-03 01:22:48 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disallow negative intervals. If aIsInterval also disallow 0,
|
|
|
|
// because we use that as a "don't repeat" flag.
|
|
|
|
interval = std::max(aIsInterval ? 1 : 0, interval);
|
|
|
|
|
|
|
|
// Make sure we don't proceed with an interval larger than our timer
|
|
|
|
// code can handle. (Note: we already forced |interval| to be non-negative,
|
|
|
|
// so the uint32_t cast (to avoid compiler warnings) is ok.)
|
|
|
|
uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
|
|
|
|
if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
|
|
|
|
interval = maxTimeoutMs;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Timeout> timeout = new Timeout();
|
|
|
|
timeout->mIsInterval = aIsInterval;
|
|
|
|
timeout->mInterval = interval;
|
|
|
|
timeout->mScriptHandler = aHandler;
|
|
|
|
timeout->mReason = aReason;
|
|
|
|
|
2016-12-22 06:34:52 +03:00
|
|
|
switch (gTimeoutBucketingStrategy) {
|
|
|
|
default:
|
|
|
|
case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: {
|
|
|
|
const char* filename = nullptr;
|
|
|
|
uint32_t dummyLine = 0, dummyColumn = 0;
|
|
|
|
aHandler->GetLocation(&filename, &dummyLine, &dummyColumn);
|
|
|
|
timeout->mIsTracking = doc->IsScriptTracking(nsDependentCString(filename));
|
2017-01-10 06:16:31 +03:00
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Classified timeout %p set from %s as %stracking\n",
|
|
|
|
timeout.get(), filename, timeout->mIsTracking ? "" : "non-"));
|
2016-12-22 06:34:52 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY:
|
|
|
|
// timeout->mIsTracking is already false!
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!timeout->mIsTracking);
|
2017-01-10 06:16:31 +03:00
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Classified timeout %p unconditionally as normal\n",
|
|
|
|
timeout.get()));
|
2016-12-22 06:34:52 +03:00
|
|
|
break;
|
|
|
|
case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY:
|
|
|
|
timeout->mIsTracking = (mTimeoutIdCounter % 2) == 0;
|
2017-01-10 06:16:31 +03:00
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Classified timeout %p as %stracking (alternating mode)\n",
|
|
|
|
timeout.get(), timeout->mIsTracking ? "" : "non-"));
|
2016-12-22 06:34:52 +03:00
|
|
|
break;
|
|
|
|
case RANDOM_TIMEOUT_BUCKETING_STRATEGY:
|
|
|
|
timeout->mIsTracking = (rand() % 2) == 0;
|
2017-01-10 06:16:31 +03:00
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Classified timeout %p as %stracking (random mode)\n",
|
|
|
|
timeout.get(), timeout->mIsTracking ? "" : "non-"));
|
2016-12-22 06:34:52 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
// Now clamp the actual interval we will use for the timer based on
|
|
|
|
uint32_t nestingLevel = sNestingLevel + 1;
|
|
|
|
uint32_t realInterval = interval;
|
|
|
|
if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL ||
|
2017-06-01 03:13:19 +03:00
|
|
|
mWindow.IsBackgroundInternal() ||
|
2017-01-21 03:36:56 +03:00
|
|
|
timeout->mIsTracking) {
|
2016-12-03 01:22:48 +03:00
|
|
|
// Don't allow timeouts less than DOMMinTimeoutValue() from
|
|
|
|
// now...
|
2016-12-22 06:40:10 +03:00
|
|
|
realInterval = std::max(realInterval,
|
|
|
|
uint32_t(DOMMinTimeoutValue(timeout->mIsTracking)));
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
timeout->mWindow = &mWindow;
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
|
|
|
|
timeout->SetWhenOrTimeRemaining(TimeStamp::Now(), delta);
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
// If we're not suspended, then set the timer.
|
|
|
|
if (!mWindow.IsSuspended()) {
|
2017-01-10 19:08:18 +03:00
|
|
|
MOZ_ASSERT(!timeout->When().IsNull());
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
nsresult rv = mExecutor->MaybeSchedule(timeout->When());
|
2016-12-03 01:22:48 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aIsInterval) {
|
|
|
|
timeout->mNestingLevel = nestingLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No popups from timeouts by default
|
|
|
|
timeout->mPopupState = openAbused;
|
|
|
|
|
|
|
|
if (gRunningTimeoutDepth == 0 &&
|
|
|
|
mWindow.GetPopupControlState() < openAbused) {
|
|
|
|
// This timeout is *not* set from another timeout and it's set
|
|
|
|
// while popups are enabled. Propagate the state to the timeout if
|
|
|
|
// its delay (interval) is equal to or less than what
|
|
|
|
// "dom.disable_open_click_delay" is set to (in ms).
|
|
|
|
|
|
|
|
int32_t delay =
|
|
|
|
Preferences::GetInt("dom.disable_open_click_delay");
|
|
|
|
|
|
|
|
// This is checking |interval|, not realInterval, on purpose,
|
|
|
|
// because our lower bound for |realInterval| could be pretty high
|
|
|
|
// in some cases.
|
|
|
|
if (interval <= delay) {
|
|
|
|
timeout->mPopupState = mWindow.GetPopupControlState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
Timeouts::SortBy sort(mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
|
|
|
: Timeouts::SortBy::TimeWhen);
|
2016-12-22 06:34:52 +03:00
|
|
|
if (timeout->mIsTracking) {
|
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
|
|
|
mTrackingTimeouts.Insert(timeout, sort);
|
|
|
|
} else {
|
|
|
|
mNormalTimeouts.Insert(timeout, sort);
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
timeout->mTimeoutId = GetTimeoutId(aReason);
|
|
|
|
*aReturn = timeout->mTimeoutId;
|
|
|
|
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
2017-02-13 23:58:39 +03:00
|
|
|
("Set%s(TimeoutManager=%p, timeout=%p, delay=%i, "
|
|
|
|
"minimum=%i, throttling=%s, background=%d, realInterval=%i) "
|
2017-01-10 06:16:31 +03:00
|
|
|
"returned %stracking timeout ID %u\n",
|
|
|
|
aIsInterval ? "Interval" : "Timeout",
|
|
|
|
this, timeout.get(), interval,
|
|
|
|
DOMMinTimeoutValue(timeout->mIsTracking),
|
2017-02-13 23:58:39 +03:00
|
|
|
mThrottleTrackingTimeouts ? "yes"
|
|
|
|
: (mThrottleTrackingTimeoutsTimer ?
|
|
|
|
"pending" : "no"),
|
2017-01-10 06:16:31 +03:00
|
|
|
int(mWindow.IsBackgroundInternal()), realInterval,
|
|
|
|
timeout->mIsTracking ? "" : "non-",
|
|
|
|
timeout->mTimeoutId));
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
|
|
|
|
{
|
|
|
|
uint32_t timerId = (uint32_t)aTimerId;
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
bool firstTimeout = true;
|
|
|
|
|
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
|
|
|
ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) {
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Clear%s(TimeoutManager=%p, timeout=%p, aTimerId=%u, ID=%u, tracking=%d)\n", aTimeout->mIsInterval ? "Interval" : "Timeout",
|
|
|
|
this, aTimeout, timerId, aTimeout->mTimeoutId,
|
|
|
|
int(aTimeout->mIsTracking)));
|
|
|
|
|
2016-12-14 02:14:02 +03:00
|
|
|
if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) {
|
|
|
|
if (aTimeout->mRunning) {
|
|
|
|
/* We're running from inside the aTimeout. Mark this
|
|
|
|
aTimeout for deferred deletion by the code in
|
2016-12-03 01:22:48 +03:00
|
|
|
RunTimeout() */
|
2016-12-14 02:14:02 +03:00
|
|
|
aTimeout->mIsInterval = false;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
else {
|
2016-12-14 02:14:02 +03:00
|
|
|
/* Delete the aTimeout from the pending aTimeout list */
|
|
|
|
aTimeout->remove();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2016-12-14 02:14:02 +03:00
|
|
|
return true; // abort!
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2017-06-01 03:13:19 +03:00
|
|
|
|
|
|
|
firstTimeout = false;
|
|
|
|
|
2016-12-14 02:14:02 +03:00
|
|
|
return false;
|
|
|
|
});
|
2017-06-01 03:13:19 +03:00
|
|
|
|
|
|
|
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()));
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-06-01 03:13:19 +03:00
|
|
|
TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadline)
|
2016-12-03 01:22:48 +03:00
|
|
|
{
|
2017-06-01 03:13:19 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!aNow.IsNull());
|
2017-06-01 03:13:18 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!aTargetDeadline.IsNull());
|
2017-06-01 03:13:18 +03:00
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
if (mWindow.IsSuspended()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(!mWindow.IsFrozen(), "Timeout running on a window in the bfcache!");
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// Limit the overall time spent in RunTimeout() to reduce jank.
|
|
|
|
uint32_t totalTimeLimitMS = std::max(1u, gMaxConsecutiveCallbacksMilliseconds);
|
|
|
|
const TimeDuration totalTimeLimit = TimeDuration::FromMilliseconds(totalTimeLimitMS);
|
|
|
|
|
|
|
|
// Allow up to 25% of our total time budget to be used figuring out which
|
|
|
|
// timers need to run. This is the initial loop in this method.
|
|
|
|
const TimeDuration initalTimeLimit =
|
|
|
|
TimeDuration::FromMilliseconds(totalTimeLimit.ToMilliseconds() / 4);
|
|
|
|
|
|
|
|
// Ammortize overhead from from calling TimeStamp::Now() in the initial
|
|
|
|
// loop, though, by only checking for an elapsed limit every N timeouts.
|
|
|
|
const uint32_t kNumTimersPerInitialElapsedCheck = 100;
|
|
|
|
|
|
|
|
// Start measuring elapsed time immediately. We won't potentially expire
|
|
|
|
// the time budget until at least one Timeout has run, though.
|
2017-06-01 03:13:19 +03:00
|
|
|
TimeStamp now(aNow);
|
|
|
|
TimeStamp start = now;
|
2017-05-19 23:45:55 +03:00
|
|
|
|
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* last_expired_normal_timeout = nullptr;
|
|
|
|
Timeout* last_expired_tracking_timeout = nullptr;
|
|
|
|
bool last_expired_timeout_is_normal = false;
|
|
|
|
Timeout* last_normal_insertion_point = nullptr;
|
|
|
|
Timeout* last_tracking_insertion_point = nullptr;
|
2017-05-19 23:45:55 +03:00
|
|
|
|
|
|
|
uint32_t firingId = CreateFiringId();
|
|
|
|
auto guard = MakeScopeExit([&] {
|
|
|
|
DestroyFiringId(firingId);
|
|
|
|
});
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
// Make sure that the window and the script context don't go away as
|
|
|
|
// a result of running timeouts
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(&mWindow);
|
|
|
|
// Silence the static analysis error about windowKungFuDeathGrip. Accessing
|
|
|
|
// members of mWindow here is safe, because the lifetime of TimeoutManager is
|
|
|
|
// the same as the lifetime of the containing nsGlobalWindow.
|
|
|
|
Unused << windowKungFuDeathGrip;
|
|
|
|
|
|
|
|
// A native timer has gone off. See which of our timeouts need
|
|
|
|
// servicing
|
|
|
|
TimeStamp deadline;
|
|
|
|
|
2017-06-01 03:13:18 +03:00
|
|
|
if (aTargetDeadline > now) {
|
2016-12-03 01:22:48 +03:00
|
|
|
// The OS timer fired early (which can happen due to the timers
|
|
|
|
// having lower precision than TimeStamp does). Set |deadline| to
|
|
|
|
// be the time when the OS timer *should* have fired so that any
|
2017-06-01 03:13:18 +03:00
|
|
|
// timers that *should* have fired *will* be fired now.
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-06-01 03:13:18 +03:00
|
|
|
deadline = aTargetDeadline;
|
2016-12-03 01:22:48 +03:00
|
|
|
} else {
|
|
|
|
deadline = now;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
TimeStamp nextDeadline;
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
// The timeout list is kept in deadline order. Discover the latest timeout
|
|
|
|
// whose deadline has expired. On some platforms, native timeout events fire
|
2017-06-01 03:13:18 +03:00
|
|
|
// "early", but we handled that above by setting deadline to aTargetDeadline
|
2016-12-03 01:22:48 +03:00
|
|
|
// if the timer fired early. So we can stop walking if we get to timeouts
|
2017-01-10 19:08:18 +03:00
|
|
|
// whose When() is greater than deadline, since once that happens we know
|
2016-12-03 01:22:48 +03:00
|
|
|
// nothing past that point is expired.
|
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
|
|
|
{
|
|
|
|
// Use a nested scope in order to make sure the strong references held by
|
|
|
|
// the iterator are freed after the loop.
|
|
|
|
OrderedTimeoutIterator expiredIter(mNormalTimeouts,
|
|
|
|
mTrackingTimeouts,
|
|
|
|
nullptr,
|
|
|
|
nullptr);
|
2017-02-28 01:33:30 +03:00
|
|
|
|
|
|
|
uint32_t numTimersToRun = 0;
|
|
|
|
|
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
|
|
|
while (true) {
|
|
|
|
Timeout* timeout = expiredIter.Next();
|
2017-01-10 19:08:18 +03:00
|
|
|
if (!timeout || timeout->When() > deadline) {
|
2017-06-01 03:13:19 +03:00
|
|
|
if (timeout) {
|
|
|
|
nextDeadline = timeout->When();
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
break;
|
|
|
|
}
|
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
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
if (IsInvalidFiringId(timeout->mFiringId)) {
|
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
|
|
|
// Mark any timeouts that are on the list to be fired with the
|
|
|
|
// firing depth so that we can reentrantly run timeouts
|
2017-05-19 23:45:55 +03:00
|
|
|
timeout->mFiringId = firingId;
|
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
|
|
|
last_expired_timeout_is_normal = expiredIter.PickedNormalIter();
|
|
|
|
if (last_expired_timeout_is_normal) {
|
|
|
|
last_expired_normal_timeout = timeout;
|
|
|
|
} else {
|
|
|
|
last_expired_tracking_timeout = timeout;
|
|
|
|
}
|
|
|
|
|
2017-03-02 19:33:11 +03:00
|
|
|
numTimersToRun += 1;
|
|
|
|
|
2017-06-01 03:13:18 +03:00
|
|
|
// Run only a limited number of timers based on the configured maximum.
|
|
|
|
if (numTimersToRun % kNumTimersPerInitialElapsedCheck == 0) {
|
2017-06-01 03:13:19 +03:00
|
|
|
now = TimeStamp::Now();
|
|
|
|
TimeDuration elapsed(now - start);
|
2017-06-01 03:13:18 +03:00
|
|
|
if (elapsed >= initalTimeLimit) {
|
2017-06-01 03:13:19 +03:00
|
|
|
nextDeadline = timeout->When();
|
2017-06-01 03:13:18 +03:00
|
|
|
break;
|
2017-05-19 23:45:55 +03:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expiredIter.UpdateIterator();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
// 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.
|
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
|
|
|
if (!last_expired_normal_timeout && !last_expired_tracking_timeout) {
|
2016-12-03 01:22:48 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert a dummy timeout into the list of timeouts between the
|
|
|
|
// portion of the list that we are about to process now and those
|
|
|
|
// timeouts that will be processed in a future call to
|
|
|
|
// win_run_timeout(). This dummy timeout serves as the head of the
|
|
|
|
// list for any timeouts inserted as a result of running a timeout.
|
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
|
|
|
RefPtr<Timeout> dummy_normal_timeout = new Timeout();
|
2017-05-19 23:45:55 +03:00
|
|
|
dummy_normal_timeout->mFiringId = firingId;
|
2017-01-10 19:08:18 +03:00
|
|
|
dummy_normal_timeout->SetDummyWhen(now);
|
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
|
|
|
if (last_expired_timeout_is_normal) {
|
|
|
|
last_expired_normal_timeout->setNext(dummy_normal_timeout);
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
RefPtr<Timeout> dummy_tracking_timeout = new Timeout();
|
2017-05-19 23:45:55 +03:00
|
|
|
dummy_tracking_timeout->mFiringId = firingId;
|
2017-01-10 19:08:18 +03:00
|
|
|
dummy_tracking_timeout->SetDummyWhen(now);
|
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
|
|
|
if (!last_expired_timeout_is_normal) {
|
|
|
|
last_expired_tracking_timeout->setNext(dummy_tracking_timeout);
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
// Now we need to search the normal and tracking timer list at the same
|
|
|
|
// time to run the timers in the scheduled order.
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
last_normal_insertion_point = mNormalTimeouts.InsertionPoint();
|
|
|
|
if (last_expired_timeout_is_normal) {
|
|
|
|
// If we ever start setting insertion point to a non-dummy timeout, the logic
|
|
|
|
// in ResetTimersForThrottleReduction will need to change.
|
|
|
|
mNormalTimeouts.SetInsertionPoint(dummy_normal_timeout);
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
last_tracking_insertion_point = mTrackingTimeouts.InsertionPoint();
|
|
|
|
if (!last_expired_timeout_is_normal) {
|
|
|
|
// If we ever start setting mTrackingTimeoutInsertionPoint to a non-dummy timeout,
|
|
|
|
// the logic in ResetTimersForThrottleReduction will need to change.
|
|
|
|
mTrackingTimeouts.SetInsertionPoint(dummy_tracking_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We stop iterating each list when we go past the last expired timeout from
|
|
|
|
// that list that we have observed above. That timeout will either be the
|
|
|
|
// dummy timeout for the list that the last expired timeout came from, or it
|
|
|
|
// will be the next item after the last timeout we looked at (or nullptr if
|
|
|
|
// we have exhausted the entire list while looking for the last expired
|
|
|
|
// timeout).
|
|
|
|
{
|
|
|
|
// Use a nested scope in order to make sure the strong references held by
|
|
|
|
// the iterator are freed after the loop.
|
|
|
|
OrderedTimeoutIterator runIter(mNormalTimeouts,
|
|
|
|
mTrackingTimeouts,
|
|
|
|
last_expired_normal_timeout ?
|
|
|
|
last_expired_normal_timeout->getNext() :
|
|
|
|
nullptr,
|
|
|
|
last_expired_tracking_timeout ?
|
|
|
|
last_expired_tracking_timeout->getNext() :
|
|
|
|
nullptr);
|
2017-05-19 23:45:55 +03:00
|
|
|
while (true) {
|
2017-06-01 03:13:18 +03:00
|
|
|
RefPtr<Timeout> timeout = runIter.Next();
|
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
|
|
|
MOZ_ASSERT(timeout != dummy_normal_timeout &&
|
|
|
|
timeout != dummy_tracking_timeout,
|
|
|
|
"We should have stopped iterating before getting to the dummy timeout");
|
|
|
|
if (!timeout) {
|
|
|
|
// We have run out of timeouts!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
runIter.UpdateIterator();
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
if (timeout->mFiringId != firingId) {
|
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 skip the timeout since it's on the list to run at another
|
|
|
|
// depth.
|
|
|
|
continue;
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
MOZ_ASSERT_IF(mWindow.IsFrozen(), mWindow.IsSuspended());
|
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
|
|
|
if (mWindow.IsSuspended()) {
|
2017-05-19 23:45:55 +03:00
|
|
|
break;
|
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
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
// The timeout is on the list to run at this depth, go ahead and
|
|
|
|
// process it.
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
// Get the script context (a strong ref to prevent it going away)
|
|
|
|
// for this timeout and ensure the script language is enabled.
|
|
|
|
nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal();
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
if (!scx) {
|
|
|
|
// No context means this window was closed or never properly
|
2017-05-19 23:45:55 +03:00
|
|
|
// initialized for this language. This timer will never fire
|
|
|
|
// so just remove it.
|
|
|
|
timeout->remove();
|
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
|
|
|
continue;
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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
|
|
|
// This timeout is good to run
|
|
|
|
bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
2017-06-01 03:13:18 +03:00
|
|
|
("Run%s(TimeoutManager=%p, timeout=%p, tracking=%d) returned %d\n", timeout->mIsInterval ? "Interval" : "Timeout",
|
2017-06-01 03:13:18 +03:00
|
|
|
this, timeout.get(),
|
2017-06-01 03:13:18 +03:00
|
|
|
int(timeout->mIsTracking),
|
2017-01-10 06:16:31 +03:00
|
|
|
!!timeout_was_cleared));
|
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
|
|
|
|
|
|
|
if (timeout_was_cleared) {
|
|
|
|
// Make sure the iterator isn't holding any Timeout objects alive.
|
|
|
|
runIter.Clear();
|
|
|
|
|
|
|
|
mNormalTimeouts.SetInsertionPoint(last_normal_insertion_point);
|
|
|
|
mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point);
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// Since ClearAllTimeouts() was called the lists should be empty.
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!HasTimeouts());
|
|
|
|
|
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
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
now = TimeStamp::Now();
|
|
|
|
|
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
|
|
|
// If we have a regular interval timer, we re-schedule the
|
|
|
|
// timeout, accounting for clock drift.
|
2017-06-01 03:13:18 +03:00
|
|
|
bool needsReinsertion = RescheduleTimeout(timeout, now);
|
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
|
|
|
|
|
|
|
// Running a timeout can cause another timeout to be deleted, so
|
|
|
|
// we need to reset the pointer to the following timeout.
|
|
|
|
runIter.UpdateIterator();
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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->remove();
|
|
|
|
|
|
|
|
if (needsReinsertion) {
|
|
|
|
// Insert interval timeout onto the corresponding list sorted in
|
|
|
|
// deadline order. AddRefs timeout.
|
|
|
|
if (runIter.PickedTrackingIter()) {
|
|
|
|
mTrackingTimeouts.Insert(timeout,
|
|
|
|
mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
|
|
|
: Timeouts::SortBy::TimeWhen);
|
|
|
|
} else {
|
|
|
|
mNormalTimeouts.Insert(timeout,
|
|
|
|
mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
|
|
|
: Timeouts::SortBy::TimeWhen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
// 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.
|
2017-06-01 03:13:19 +03:00
|
|
|
TimeDuration elapsed = now - start;
|
2017-06-01 03:13:18 +03:00
|
|
|
if (elapsed >= totalTimeLimit) {
|
2017-06-01 03:13:19 +03:00
|
|
|
// 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()));
|
|
|
|
}
|
2017-06-01 03:13:18 +03:00
|
|
|
break;
|
2017-05-19 23:45:55 +03:00
|
|
|
}
|
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
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Take the dummy timeout off the head of the list
|
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
|
|
|
if (dummy_normal_timeout->isInList()) {
|
|
|
|
dummy_normal_timeout->remove();
|
|
|
|
}
|
|
|
|
if (dummy_tracking_timeout->isInList()) {
|
|
|
|
dummy_tracking_timeout->remove();
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
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.SetInsertionPoint(last_normal_insertion_point);
|
|
|
|
mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point);
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2017-06-01 03:13:18 +03:00
|
|
|
TimeoutManager::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now)
|
2016-12-03 01:22:48 +03:00
|
|
|
{
|
|
|
|
if (!aTimeout->mIsInterval) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute time to next timeout for interval timer.
|
|
|
|
// Make sure nextInterval is at least DOMMinTimeoutValue().
|
|
|
|
TimeDuration nextInterval =
|
2016-12-22 06:40:10 +03:00
|
|
|
TimeDuration::FromMilliseconds(
|
|
|
|
std::max(aTimeout->mInterval,
|
|
|
|
uint32_t(DOMMinTimeoutValue(aTimeout->mIsTracking))));
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-06-01 03:13:18 +03:00
|
|
|
TimeStamp firingTime = now + nextInterval;
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
TimeStamp currentNow = TimeStamp::Now();
|
|
|
|
TimeDuration delay = firingTime - currentNow;
|
|
|
|
|
|
|
|
// And make sure delay is nonnegative; that might happen if the timer
|
|
|
|
// thread is firing our timers somewhat early or if they're taking a long
|
|
|
|
// time to run the callback.
|
|
|
|
if (delay < TimeDuration(0)) {
|
|
|
|
delay = TimeDuration(0);
|
|
|
|
}
|
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
aTimeout->SetWhenOrTimeRemaining(currentNow, delay);
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
if (mWindow.IsSuspended()) {
|
2016-12-03 01:22:48 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
nsresult rv = mExecutor->MaybeSchedule(aTimeout->When());
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TimeoutManager::ResetTimersForThrottleReduction()
|
|
|
|
{
|
|
|
|
return ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
|
|
|
|
|
|
|
|
if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-12-14 03:06:21 +03:00
|
|
|
Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
|
|
|
: Timeouts::SortBy::TimeWhen;
|
|
|
|
|
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
|
|
|
nsresult rv = mNormalTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
|
2016-12-22 06:40:10 +03:00
|
|
|
*this,
|
2017-06-01 03:13:19 +03:00
|
|
|
sortBy);
|
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
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = mTrackingTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
|
2016-12-22 06:40:10 +03:00
|
|
|
*this,
|
2017-06-01 03:13:19 +03:00
|
|
|
sortBy);
|
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
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
OrderedTimeoutIterator iter(mNormalTimeouts,
|
|
|
|
mTrackingTimeouts,
|
|
|
|
nullptr,
|
|
|
|
nullptr);
|
|
|
|
Timeout* firstTimeout = iter.Next();
|
|
|
|
if (firstTimeout) {
|
|
|
|
rv = mExecutor->MaybeSchedule(firstTimeout->When());
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
return NS_OK;
|
2016-12-14 03:06:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
|
2016-12-22 06:40:10 +03:00
|
|
|
const TimeoutManager& aTimeoutManager,
|
2017-06-01 03:13:19 +03:00
|
|
|
SortBy aSortBy)
|
2016-12-14 03:06:21 +03:00
|
|
|
{
|
2016-12-03 01:22:48 +03:00
|
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
|
2016-12-13 18:23:03 +03:00
|
|
|
// 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;
|
2017-01-10 19:08:18 +03:00
|
|
|
// insertion point itself is a dummy timeout with an When() that may be
|
2016-12-13 18:23:03 +03:00
|
|
|
// 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.
|
2016-12-03 01:22:48 +03:00
|
|
|
// Otherwise, start at the beginning of the list.
|
2017-06-01 03:13:18 +03:00
|
|
|
for (RefPtr<Timeout> timeout = InsertionPoint() ?
|
2016-12-14 03:06:21 +03:00
|
|
|
InsertionPoint()->getNext() : GetFirst();
|
2016-12-03 01:22:48 +03:00
|
|
|
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
|
2017-01-10 19:08:18 +03:00
|
|
|
// timeout->When() below.
|
|
|
|
if (timeout->When() <= now) {
|
2016-12-03 01:22:48 +03:00
|
|
|
timeout = timeout->getNext();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
if (timeout->When() - now >
|
2016-12-03 01:22:48 +03:00
|
|
|
TimeDuration::FromMilliseconds(aPreviousThrottleDelayMS)) {
|
2017-01-10 19:08:18 +03:00
|
|
|
// No need to loop further. Timeouts are sorted in When() order
|
2016-12-03 01:22:48 +03:00
|
|
|
// and the ones after this point were all set up for at least
|
|
|
|
// gMinBackgroundTimeoutValue ms and hence were not clamped.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We reduced our throttled delay. Re-init the timer appropriately.
|
|
|
|
// Compute the interval the timer should have had if it had not been set in a
|
|
|
|
// background window
|
|
|
|
TimeDuration interval =
|
2016-12-22 06:40:10 +03:00
|
|
|
TimeDuration::FromMilliseconds(
|
|
|
|
std::max(timeout->mInterval,
|
|
|
|
uint32_t(aTimeoutManager.
|
|
|
|
DOMMinTimeoutValue(timeout->mIsTracking))));
|
2017-06-01 03:13:18 +03:00
|
|
|
const TimeDuration& oldInterval = timeout->ScheduledDelay();
|
2016-12-03 01:22:48 +03:00
|
|
|
if (oldInterval > interval) {
|
|
|
|
// unclamp
|
|
|
|
TimeStamp firingTime =
|
2017-01-10 19:08:18 +03:00
|
|
|
std::max(timeout->When() - oldInterval + interval, now);
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
NS_ASSERTION(firingTime < timeout->When(),
|
2016-12-03 01:22:48 +03:00
|
|
|
"Our firing time should strictly decrease!");
|
|
|
|
|
|
|
|
TimeDuration delay = firingTime - now;
|
2017-01-10 19:08:18 +03:00
|
|
|
timeout->SetWhenOrTimeRemaining(now, delay);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(timeout->When() == firingTime);
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
// Since we reset When() we need to move |timeout| to the right
|
|
|
|
// place in the list so that it remains sorted by When().
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
// Get the pointer to the next timeout now, before we move the
|
|
|
|
// current timeout in the list.
|
|
|
|
Timeout* nextTimeout = timeout->getNext();
|
|
|
|
|
2017-02-06 20:10:00 +03:00
|
|
|
// Since we are only reducing intervals in this method we can
|
|
|
|
// make an optimization here. If the reduction does not cause us
|
|
|
|
// to fall before our previous timeout then we do not have to
|
|
|
|
// remove and re-insert the current timeout. This is important
|
|
|
|
// because re-insertion makes this algorithm O(n^2). Since we
|
|
|
|
// will typically be shifting a lot of timers at once this
|
|
|
|
// optimization saves us a lot of work.
|
|
|
|
Timeout* prevTimeout = timeout->getPrevious();
|
|
|
|
if (prevTimeout && prevTimeout->When() > timeout->When()) {
|
|
|
|
// It is safe to remove and re-insert because When() is now
|
|
|
|
// strictly smaller than it used to be, so we know we'll insert
|
|
|
|
// |timeout| before nextTimeout.
|
|
|
|
NS_ASSERTION(!nextTimeout ||
|
|
|
|
timeout->When() < nextTimeout->When(), "How did that happen?");
|
|
|
|
timeout->remove();
|
2017-06-01 03:13:18 +03:00
|
|
|
// Insert() will reset mFiringId. Make sure to undo that.
|
2017-05-19 23:45:55 +03:00
|
|
|
uint32_t firingId = timeout->mFiringId;
|
2017-02-06 20:10:00 +03:00
|
|
|
Insert(timeout, aSortBy);
|
2017-05-19 23:45:55 +03:00
|
|
|
timeout->mFiringId = firingId;
|
2017-02-06 20:10:00 +03:00
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
timeout = nextTimeout;
|
|
|
|
} else {
|
|
|
|
timeout = timeout->getNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::ClearAllTimeouts()
|
|
|
|
{
|
2016-12-14 02:14:02 +03:00
|
|
|
bool seenRunningTimeout = false;
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("ClearAllTimeouts(TimeoutManager=%p)\n", this));
|
|
|
|
|
2017-04-25 12:20:00 +03:00
|
|
|
if (mThrottleTrackingTimeoutsTimer) {
|
|
|
|
mThrottleTrackingTimeoutsTimer->Cancel();
|
|
|
|
mThrottleTrackingTimeoutsTimer = nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
mExecutor->Cancel();
|
|
|
|
|
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
|
|
|
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
2016-12-03 01:22:48 +03:00
|
|
|
/* 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. */
|
2016-12-14 02:14:02 +03:00
|
|
|
if (mRunningTimeout == aTimeout) {
|
|
|
|
seenRunningTimeout = true;
|
2016-12-13 18:23:03 +03:00
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
|
|
|
|
// Set timeout->mCleared to true to indicate that the timeout was
|
|
|
|
// cleared and taken out of the list of timeouts
|
2016-12-14 02:14:02 +03:00
|
|
|
aTimeout->mCleared = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (seenRunningTimeout) {
|
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.SetInsertionPoint(nullptr);
|
|
|
|
mTrackingTimeouts.SetInsertionPoint(nullptr);
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clear out our list
|
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.Clear();
|
|
|
|
mTrackingTimeouts.Clear();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-12-14 03:06:21 +03:00
|
|
|
TimeoutManager::Timeouts::Insert(Timeout* aTimeout, SortBy aSortBy)
|
2016-12-03 01:22:48 +03:00
|
|
|
{
|
2017-06-01 03:13:18 +03:00
|
|
|
|
2016-12-13 18:23:03 +03:00
|
|
|
// 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.
|
2016-12-03 01:22:48 +03:00
|
|
|
Timeout* prevSibling;
|
2016-12-14 03:06:21 +03:00
|
|
|
for (prevSibling = GetLast();
|
|
|
|
prevSibling && prevSibling != InsertionPoint() &&
|
2016-12-03 01:22:48 +03:00
|
|
|
// This condition needs to match the one in SetTimeoutOrInterval that
|
2017-01-10 19:08:18 +03:00
|
|
|
// determines whether to set When() or TimeRemaining().
|
2016-12-14 03:06:21 +03:00
|
|
|
(aSortBy == SortBy::TimeRemaining ?
|
2017-01-10 19:08:18 +03:00
|
|
|
prevSibling->TimeRemaining() > aTimeout->TimeRemaining() :
|
|
|
|
prevSibling->When() > aTimeout->When());
|
2016-12-03 01:22:48 +03:00
|
|
|
prevSibling = prevSibling->getPrevious()) {
|
|
|
|
/* Do nothing; just searching */
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now link in aTimeout after prevSibling.
|
|
|
|
if (prevSibling) {
|
|
|
|
prevSibling->setNext(aTimeout);
|
|
|
|
} else {
|
2016-12-14 03:06:21 +03:00
|
|
|
InsertFront(aTimeout);
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
2017-05-19 23:45:55 +03:00
|
|
|
aTimeout->mFiringId = InvalidFiringId;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Timeout*
|
|
|
|
TimeoutManager::BeginRunningTimeout(Timeout* aTimeout)
|
|
|
|
{
|
|
|
|
Timeout* currentTimeout = mRunningTimeout;
|
|
|
|
mRunningTimeout = aTimeout;
|
|
|
|
|
|
|
|
++gRunningTimeoutDepth;
|
|
|
|
|
2017-05-02 14:23:00 +03:00
|
|
|
if (!mWindow.IsChromeWindow()) {
|
|
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
if (currentTimeout) {
|
|
|
|
// If we're already running a timeout and start running another
|
|
|
|
// one, record the fragment duration already collected.
|
|
|
|
TimeoutTelemetry::Get().RecordExecution(
|
|
|
|
now, currentTimeout, IsBackground());
|
|
|
|
}
|
|
|
|
|
|
|
|
TimeoutTelemetry::Get().MaybeCollectTelemetry(now);
|
|
|
|
TimeoutTelemetry::Get().StartRecording(now);
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
return currentTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::EndRunningTimeout(Timeout* aTimeout)
|
|
|
|
{
|
|
|
|
--gRunningTimeoutDepth;
|
|
|
|
|
2017-05-02 14:23:00 +03:00
|
|
|
if (!mWindow.IsChromeWindow()) {
|
|
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
TimeoutTelemetry::Get().RecordExecution(now, mRunningTimeout, IsBackground());
|
|
|
|
|
|
|
|
if (aTimeout) {
|
|
|
|
// If we were running a nested timeout, restart the measurement
|
|
|
|
// from here.
|
|
|
|
TimeoutTelemetry::Get().StartRecording(now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
mRunningTimeout = aTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::UnmarkGrayTimers()
|
|
|
|
{
|
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
|
|
|
ForEachUnorderedTimeout([](Timeout* aTimeout) {
|
2016-12-14 02:14:02 +03:00
|
|
|
if (aTimeout->mScriptHandler) {
|
|
|
|
aTimeout->mScriptHandler->MarkForCC();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2016-12-14 02:14:02 +03:00
|
|
|
});
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::Suspend()
|
|
|
|
{
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Suspend(TimeoutManager=%p)\n", this));
|
|
|
|
|
2017-04-25 12:20:00 +03:00
|
|
|
if (mThrottleTrackingTimeoutsTimer) {
|
|
|
|
mThrottleTrackingTimeoutsTimer->Cancel();
|
|
|
|
mThrottleTrackingTimeoutsTimer = nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
mExecutor->Cancel();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::Resume()
|
|
|
|
{
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Resume(TimeoutManager=%p)\n", this));
|
|
|
|
|
2017-04-25 12:20:00 +03:00
|
|
|
// When Suspend() has been called after IsDocumentLoaded(), but the
|
|
|
|
// throttle tracking timer never managed to fire, start the timer
|
|
|
|
// again.
|
|
|
|
if (mWindow.AsInner()->IsDocumentLoaded() && !mThrottleTrackingTimeouts) {
|
|
|
|
MaybeStartThrottleTrackingTimout();
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
DebugOnly<bool> _seenDummyTimeout = false;
|
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
TimeStamp nextWakeUp;
|
|
|
|
|
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
|
|
|
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
2016-12-03 01:22:48 +03:00
|
|
|
// 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.
|
2016-12-14 02:14:02 +03:00
|
|
|
if (!aTimeout->mWindow) {
|
2016-12-03 01:22:48 +03:00
|
|
|
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
|
|
|
|
_seenDummyTimeout = true;
|
2016-12-14 02:14:02 +03:00
|
|
|
return;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
// The timeout When() is set to the absolute time when the timer should
|
2016-12-03 01:22:48 +03:00
|
|
|
// fire. Recalculate the delay from now until that deadline. If the
|
|
|
|
// the deadline has already passed or falls within our minimum delay
|
2017-06-01 03:13:19 +03:00
|
|
|
// deadline, then clamp the resulting value to the minimum delay.
|
2016-12-03 01:22:48 +03:00
|
|
|
int32_t remaining = 0;
|
2017-01-10 19:08:18 +03:00
|
|
|
if (aTimeout->When() > now) {
|
|
|
|
remaining = static_cast<int32_t>((aTimeout->When() - now).ToMilliseconds());
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2016-12-22 06:40:10 +03:00
|
|
|
uint32_t delay = std::max(remaining, DOMMinTimeoutValue(aTimeout->mIsTracking));
|
2017-06-01 03:13:19 +03:00
|
|
|
aTimeout->SetWhenOrTimeRemaining(now, TimeDuration::FromMilliseconds(delay));
|
2016-12-03 01:22:48 +03:00
|
|
|
|
2017-06-01 03:13:19 +03:00
|
|
|
if (nextWakeUp.IsNull() || aTimeout->When() < nextWakeUp) {
|
|
|
|
nextWakeUp = aTimeout->When();
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2016-12-14 02:14:02 +03:00
|
|
|
});
|
2017-06-01 03:13:19 +03:00
|
|
|
|
|
|
|
if (!nextWakeUp.IsNull()) {
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mExecutor->MaybeSchedule(nextWakeUp));
|
|
|
|
}
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::Freeze()
|
|
|
|
{
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Freeze(TimeoutManager=%p)\n", this));
|
|
|
|
|
2017-03-13 22:26:22 +03:00
|
|
|
DebugOnly<bool> _seenDummyTimeout = false;
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
TimeStamp now = TimeStamp::Now();
|
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
|
|
|
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
2017-03-13 22:26:22 +03:00
|
|
|
if (!aTimeout->mWindow) {
|
|
|
|
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
|
|
|
|
_seenDummyTimeout = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
// 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.
|
2017-01-10 19:08:18 +03:00
|
|
|
TimeDuration delta(0);
|
|
|
|
if (aTimeout->When() > now) {
|
|
|
|
delta = aTimeout->When() - now;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2017-01-10 19:08:18 +03:00
|
|
|
aTimeout->SetWhenOrTimeRemaining(now, delta);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aTimeout->TimeRemaining() == delta);
|
2016-12-14 02:14:02 +03:00
|
|
|
});
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::Thaw()
|
|
|
|
{
|
2017-01-10 06:16:31 +03:00
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("Thaw(TimeoutManager=%p)\n", this));
|
|
|
|
|
2016-12-03 01:22:48 +03:00
|
|
|
TimeStamp now = TimeStamp::Now();
|
|
|
|
DebugOnly<bool> _seenDummyTimeout = false;
|
|
|
|
|
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
|
|
|
ForEachUnorderedTimeout([&](Timeout* aTimeout) {
|
2016-12-03 01:22:48 +03:00
|
|
|
// 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.
|
2016-12-14 02:14:02 +03:00
|
|
|
if (!aTimeout->mWindow) {
|
2016-12-03 01:22:48 +03:00
|
|
|
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
|
|
|
|
_seenDummyTimeout = true;
|
2016-12-14 02:14:02 +03:00
|
|
|
return;
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 19:08:18 +03:00
|
|
|
// Set When() back to the time when the timer is supposed to fire.
|
|
|
|
aTimeout->SetWhenOrTimeRemaining(now, aTimeout->TimeRemaining());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!aTimeout->When().IsNull());
|
2016-12-14 02:14:02 +03:00
|
|
|
});
|
2016-12-03 01:22:48 +03:00
|
|
|
}
|
2016-12-01 03:32:23 +03:00
|
|
|
|
|
|
|
bool
|
|
|
|
TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId)
|
|
|
|
{
|
|
|
|
return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
|
|
|
|
return aTimeout->mTimeoutId == aTimeoutId;
|
|
|
|
});
|
|
|
|
}
|
2017-02-13 23:58:39 +03:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class ThrottleTrackingTimeoutsCallback final : public nsITimerCallback
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ThrottleTrackingTimeoutsCallback(nsGlobalWindow* aWindow)
|
|
|
|
: mWindow(aWindow)
|
|
|
|
{
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aWindow->IsInnerWindow());
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSITIMERCALLBACK
|
|
|
|
|
|
|
|
private:
|
|
|
|
~ThrottleTrackingTimeoutsCallback() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The strong reference here keeps the Window and hence the TimeoutManager
|
|
|
|
// object itself alive.
|
|
|
|
RefPtr<nsGlobalWindow> mWindow;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(ThrottleTrackingTimeoutsCallback, nsITimerCallback)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
ThrottleTrackingTimeoutsCallback::Notify(nsITimer* aTimer)
|
|
|
|
{
|
|
|
|
mWindow->AsInner()->TimeoutManager().StartThrottlingTrackingTimeouts();
|
|
|
|
mWindow = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::StartThrottlingTrackingTimeouts()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mThrottleTrackingTimeoutsTimer);
|
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("TimeoutManager %p started to throttle tracking timeouts\n", this));
|
|
|
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mThrottleTrackingTimeouts);
|
|
|
|
mThrottleTrackingTimeouts = true;
|
|
|
|
mThrottleTrackingTimeoutsTimer = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::OnDocumentLoaded()
|
2017-04-25 12:20:00 +03:00
|
|
|
{
|
2017-05-26 07:40:12 +03:00
|
|
|
// The load event may be firing again if we're coming back to the page by
|
|
|
|
// navigating through the session history, so we need to ensure to only call
|
|
|
|
// this when mThrottleTrackingTimeouts hasn't been set yet.
|
|
|
|
if (!mThrottleTrackingTimeouts) {
|
|
|
|
MaybeStartThrottleTrackingTimout();
|
|
|
|
}
|
2017-04-25 12:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::MaybeStartThrottleTrackingTimout()
|
2017-02-13 23:58:39 +03:00
|
|
|
{
|
2017-05-15 14:06:28 +03:00
|
|
|
if (gTrackingTimeoutThrottlingDelay <= 0 ||
|
2017-05-23 16:36:08 +03:00
|
|
|
mWindow.AsInner()->InnerObjectsFreed() || mWindow.IsSuspended()) {
|
2017-02-13 23:58:39 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mThrottleTrackingTimeouts);
|
|
|
|
|
|
|
|
MOZ_LOG(gLog, LogLevel::Debug,
|
|
|
|
("TimeoutManager %p delaying tracking timeout throttling by %dms\n",
|
|
|
|
this, gTrackingTimeoutThrottlingDelay));
|
|
|
|
|
|
|
|
mThrottleTrackingTimeoutsTimer =
|
|
|
|
do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
if (!mThrottleTrackingTimeoutsTimer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsITimerCallback> callback =
|
|
|
|
new ThrottleTrackingTimeoutsCallback(&mWindow);
|
|
|
|
|
|
|
|
mThrottleTrackingTimeoutsTimer->InitWithCallback(callback,
|
|
|
|
gTrackingTimeoutThrottlingDelay,
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
|
|
}
|
2017-05-02 14:23:00 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::BeginSyncOperation()
|
|
|
|
{
|
|
|
|
// If we're beginning a sync operation, the currently running
|
|
|
|
// timeout will be put on hold. To not get into an inconsistent
|
|
|
|
// state, where the currently running timeout appears to take time
|
|
|
|
// equivalent to the period of us spinning up a new event loop,
|
|
|
|
// record what we have and stop recording until we reach
|
|
|
|
// EndSyncOperation.
|
|
|
|
if (!mWindow.IsChromeWindow()) {
|
|
|
|
if (mRunningTimeout) {
|
|
|
|
TimeoutTelemetry::Get().RecordExecution(
|
|
|
|
TimeStamp::Now(), mRunningTimeout, IsBackground());
|
|
|
|
}
|
|
|
|
TimeoutTelemetry::Get().StopRecording();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TimeoutManager::EndSyncOperation()
|
|
|
|
{
|
|
|
|
// If we're running a timeout, restart the measurement from here.
|
|
|
|
if (!mWindow.IsChromeWindow() && mRunningTimeout) {
|
|
|
|
TimeoutTelemetry::Get().StartRecording(TimeStamp::Now());
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 03:13:19 +03:00
|
|
|
|
|
|
|
nsIEventTarget*
|
|
|
|
TimeoutManager::EventTarget()
|
|
|
|
{
|
|
|
|
return mWindow.EventTargetFor(TaskCategory::Timer);
|
|
|
|
}
|