mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Backed out 2 changesets (bug 1367905) for a spike in Windows reftest failures a=backout
Backed out changeset c5aaa3f7b79e (bug 1367905) Backed out changeset 1c66da2b1e88 (bug 1367905) MozReview-Commit-ID: IX632WoWHrO
This commit is contained in:
parent
a798ee4fc3
commit
43acd1b6e6
@ -56,8 +56,6 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsScriptNameSpaceManager.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/MainThreadIdlePeriod.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/DOMExceptionBinding.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
@ -65,7 +63,7 @@
|
||||
#include "nsAXPCNativeCallContext.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
#include "nsJSPrincipals.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
@ -113,24 +111,21 @@ const size_t gStackSize = 8192;
|
||||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||
|
||||
// If we haven't painted in 100ms, or we're in e10s parent process and
|
||||
// user isn't active, we allow for a longer GC budget.
|
||||
#define NS_INTERSLICE_GC_BUDGET 40 // ms
|
||||
|
||||
// The amount of time we wait between a request to CC (after GC ran)
|
||||
// and doing the actual CC.
|
||||
#define NS_CC_DELAY 6000 // ms
|
||||
|
||||
#define NS_CC_SKIPPABLE_DELAY 250 // ms
|
||||
|
||||
// ForgetSkippable is usually fast, so we can use small budgets.
|
||||
// This isn't a real budget but a hint to CollectorRunner whether there
|
||||
// is enough time to call ForgetSkippable.
|
||||
static const int64_t kForgetSkippableSliceDuration = 2;
|
||||
|
||||
// Maximum amount of time that should elapse between incremental CC slices
|
||||
static const int64_t kICCIntersliceDelay = 32; // ms
|
||||
|
||||
// Time budget for an incremental CC slice when using timer to run it.
|
||||
// Time budget for an incremental CC slice
|
||||
static const int64_t kICCSliceBudget = 5; // ms
|
||||
// Minimum budget for an incremental CC slice when using idle time to run it.
|
||||
static const int64_t kIdleICCSliceBudget = 3; // ms
|
||||
|
||||
// Maximum total duration for an ICC
|
||||
static const uint32_t kMaxICCDuration = 2000; // ms
|
||||
@ -149,16 +144,14 @@ static const uint32_t kMaxICCDuration = 2000; // ms
|
||||
// Large value used to specify that a script should run essentially forever
|
||||
#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
|
||||
|
||||
class CollectorRunner;
|
||||
|
||||
// if you add statics here, add them to the list in StartupJSEnvironment
|
||||
|
||||
static nsITimer *sGCTimer;
|
||||
static nsITimer *sShrinkingGCTimer;
|
||||
static StaticRefPtr<CollectorRunner> sCCRunner;
|
||||
static StaticRefPtr<CollectorRunner> sICCRunner;
|
||||
static nsITimer *sCCTimer;
|
||||
static nsITimer *sICCTimer;
|
||||
static nsITimer *sFullGCTimer;
|
||||
static StaticRefPtr<CollectorRunner> sInterSliceGCRunner;
|
||||
static nsITimer *sInterSliceGCTimer;
|
||||
|
||||
static TimeStamp sLastCCEndTime;
|
||||
|
||||
@ -183,7 +176,7 @@ static uint32_t sCCollectedZonesWaitingForGC;
|
||||
static uint32_t sLikelyShortLivingObjectsNeedingGC;
|
||||
static bool sPostGCEventsToConsole;
|
||||
static bool sPostGCEventsToObserver;
|
||||
static int32_t sCCRunnerFireCount = 0;
|
||||
static int32_t sCCTimerFireCount = 0;
|
||||
static uint32_t sMinForgetSkippableTime = UINT32_MAX;
|
||||
static uint32_t sMaxForgetSkippableTime = 0;
|
||||
static uint32_t sTotalForgetSkippableTime = 0;
|
||||
@ -195,6 +188,7 @@ static bool sNeedsFullCC = false;
|
||||
static bool sNeedsFullGC = false;
|
||||
static bool sNeedsGCAfterCC = false;
|
||||
static bool sIncrementalCC = false;
|
||||
static bool sDidPaintAfterPreviousICCSlice = false;
|
||||
static int32_t sActiveIntersliceGCBudget = 0; // ms;
|
||||
static nsScriptNameSpaceManager *gNameSpaceManager;
|
||||
|
||||
@ -229,178 +223,6 @@ static bool sIsCompactingOnUserInactive = false;
|
||||
static int32_t sExpensiveCollectorPokes = 0;
|
||||
static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
|
||||
|
||||
// Return true if some meaningful work was done.
|
||||
typedef bool (*CollectorRunnerCallback) (TimeStamp aDeadline, void* aData);
|
||||
|
||||
// Repeating callback runner for CC and GC.
|
||||
class CollectorRunner final : public IdleRunnable
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<CollectorRunner>
|
||||
Create(CollectorRunnerCallback aCallback, uint32_t aDelay,
|
||||
int64_t aBudget, bool aRepeating, void* aData = nullptr)
|
||||
{
|
||||
RefPtr<CollectorRunner> runner =
|
||||
new CollectorRunner(aCallback, aDelay, aBudget, aRepeating, aData);
|
||||
runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
|
||||
return runner.forget();
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (!mCallback) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Deadline is null when called from timer.
|
||||
bool deadLineWasNull = mDeadline.IsNull();
|
||||
bool didRun = false;
|
||||
if (deadLineWasNull || ((TimeStamp::Now() + mBudget) < mDeadline)) {
|
||||
CancelTimer();
|
||||
didRun = mCallback(mDeadline, mData);
|
||||
}
|
||||
|
||||
if (mCallback && (mRepeating || !didRun)) {
|
||||
// If we didn't do meaningful work, don't schedule using immediate
|
||||
// idle dispatch, since that could lead to a loop until the idle
|
||||
// period ends.
|
||||
Schedule(didRun);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
TimedOut(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
|
||||
runnable->Run();
|
||||
}
|
||||
|
||||
void SetDeadline(mozilla::TimeStamp aDeadline) override
|
||||
{
|
||||
mDeadline = aDeadline;
|
||||
};
|
||||
|
||||
void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override
|
||||
{
|
||||
if (mTimerActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTarget = aTarget;
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
} else {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->SetTarget(mTarget);
|
||||
mTimer->InitWithFuncCallback(TimedOut, this, aDelay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
mTimerActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
CancelTimer();
|
||||
mTimer = nullptr;
|
||||
mScheduleTimer = nullptr;
|
||||
mCallback = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
|
||||
runnable->Schedule(true);
|
||||
}
|
||||
|
||||
void Schedule(bool aAllowIdleDispatch)
|
||||
{
|
||||
if (!mCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sShuttingDown) {
|
||||
Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
mDeadline = TimeStamp();
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
|
||||
if (hint != now) {
|
||||
// RefreshDriver is ticking, let it schedule the idle dispatch.
|
||||
nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
|
||||
// Ensure we get called at some point, even if RefreshDriver is stopped.
|
||||
SetTimer(mDelay, mTarget);
|
||||
} else {
|
||||
// RefreshDriver doesn't seem to be running.
|
||||
if (aAllowIdleDispatch) {
|
||||
nsCOMPtr<nsIRunnable> runnable = this;
|
||||
NS_IdleDispatchToCurrentThread(runnable.forget(), mDelay);
|
||||
SetTimer(mDelay, mTarget);
|
||||
} else {
|
||||
if (!mScheduleTimer) {
|
||||
mScheduleTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!mScheduleTimer) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mScheduleTimer->Cancel();
|
||||
}
|
||||
|
||||
// We weren't allowed to do idle dispatch immediately, do it after a
|
||||
// short timeout.
|
||||
mScheduleTimer->InitWithFuncCallback(ScheduleTimedOut, this, 16,
|
||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
explicit CollectorRunner(CollectorRunnerCallback aCallback,
|
||||
uint32_t aDelay, int64_t aBudget,
|
||||
bool aRepeating, void* aData)
|
||||
: mCallback(aCallback), mDelay(aDelay)
|
||||
, mBudget(TimeDuration::FromMilliseconds(aBudget))
|
||||
, mRepeating(aRepeating), mTimerActive(false), mData(aData)
|
||||
{
|
||||
}
|
||||
|
||||
~CollectorRunner()
|
||||
{
|
||||
CancelTimer();
|
||||
}
|
||||
|
||||
void CancelTimer()
|
||||
{
|
||||
nsRefreshDriver::CancelIdleRunnable(this);
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
if (mScheduleTimer) {
|
||||
mScheduleTimer->Cancel();
|
||||
}
|
||||
mTimerActive = false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsITimer> mScheduleTimer;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
CollectorRunnerCallback mCallback;
|
||||
uint32_t mDelay;
|
||||
TimeStamp mDeadline;
|
||||
TimeDuration mBudget;
|
||||
bool mRepeating;
|
||||
bool mTimerActive;
|
||||
void* mData;
|
||||
};
|
||||
|
||||
static const char*
|
||||
ProcessNameForCollectorLog()
|
||||
{
|
||||
@ -477,10 +299,10 @@ KillTimers()
|
||||
{
|
||||
nsJSContext::KillGCTimer();
|
||||
nsJSContext::KillShrinkingGCTimer();
|
||||
nsJSContext::KillCCRunner();
|
||||
nsJSContext::KillICCRunner();
|
||||
nsJSContext::KillCCTimer();
|
||||
nsJSContext::KillICCTimer();
|
||||
nsJSContext::KillFullGCTimer();
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
}
|
||||
|
||||
// If we collected a substantial amount of cycles, poke the GC since more objects
|
||||
@ -1629,7 +1451,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
||||
nsJSContext::RunCycleCollectorSlice()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
@ -1645,40 +1467,33 @@ nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
||||
js::SliceBudget budget = js::SliceBudget::unlimited();
|
||||
|
||||
if (sIncrementalCC) {
|
||||
int64_t baseBudget = kICCSliceBudget;
|
||||
if (!aDeadline.IsNull()) {
|
||||
baseBudget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
|
||||
}
|
||||
|
||||
if (gCCStats.mBeginTime.IsNull()) {
|
||||
// If no CC is in progress, use the standard slice time.
|
||||
budget = js::SliceBudget(js::TimeBudget(baseBudget));
|
||||
budget = js::SliceBudget(js::TimeBudget(kICCSliceBudget));
|
||||
} else {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
// Only run a limited slice if we're within the max running time.
|
||||
uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
|
||||
if (runningTime < kMaxICCDuration) {
|
||||
const float maxSlice = MainThreadIdlePeriod::GetLongIdlePeriod();
|
||||
|
||||
// Try to make up for a delay in running this slice.
|
||||
float sliceDelayMultiplier =
|
||||
TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
|
||||
float delaySliceBudget =
|
||||
std::min(baseBudget * sliceDelayMultiplier, maxSlice);
|
||||
float sliceDelayMultiplier = TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
|
||||
float delaySliceBudget = kICCSliceBudget * sliceDelayMultiplier;
|
||||
|
||||
// Increase slice budgets up to |maxSlice| as we approach
|
||||
// Increase slice budgets up to |maxLaterSlice| as we approach
|
||||
// half way through the ICC, to avoid large sync CCs.
|
||||
float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
|
||||
float laterSliceBudget = maxSlice * percentToHalfDone;
|
||||
const float maxLaterSlice = 40.0f;
|
||||
float laterSliceBudget = maxLaterSlice * percentToHalfDone;
|
||||
|
||||
budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
|
||||
laterSliceBudget, (float)baseBudget})));
|
||||
laterSliceBudget, (float)kICCSliceBudget})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCycleCollector_collectSlice(budget);
|
||||
nsCycleCollector_collectSlice(budget, sDidPaintAfterPreviousICCSlice);
|
||||
sDidPaintAfterPreviousICCSlice = false;
|
||||
|
||||
gCCStats.FinishCycleCollectionSlice();
|
||||
}
|
||||
@ -1714,11 +1529,11 @@ nsJSContext::GetMaxCCSliceTimeSinceClear()
|
||||
return gCCStats.mMaxSliceTimeSinceClear;
|
||||
}
|
||||
|
||||
static bool
|
||||
ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
static void
|
||||
ICCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
|
||||
@ -1728,15 +1543,14 @@ ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
PRTime now = PR_Now();
|
||||
if (sCCLockedOutTime == 0) {
|
||||
sCCLockedOutTime = now;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||
return true;
|
||||
nsJSContext::RunCycleCollectorSlice();
|
||||
}
|
||||
|
||||
//static
|
||||
@ -1748,16 +1562,22 @@ nsJSContext::BeginCycleCollectionCallback()
|
||||
gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
|
||||
gCCStats.mSuspected = nsCycleCollector_suspectedCount();
|
||||
|
||||
KillCCRunner();
|
||||
KillCCTimer();
|
||||
|
||||
gCCStats.RunForgetSkippable();
|
||||
|
||||
MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
|
||||
MOZ_ASSERT(!sICCTimer, "Tried to create a new ICC timer when one already existed.");
|
||||
|
||||
// Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
|
||||
// an incremental collection, and we want to be sure to finish it.
|
||||
sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
|
||||
kIdleICCSliceBudget, true);
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
|
||||
if (sICCTimer) {
|
||||
sICCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
||||
kICCIntersliceDelay,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"ICCTimerFired");
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
|
||||
@ -1768,7 +1588,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsJSContext::KillICCRunner();
|
||||
nsJSContext::KillICCTimer();
|
||||
|
||||
// Update timing information for the current slice before we log it, if
|
||||
// we previously called PrepareForCycleCollectionSlice(). During shutdown
|
||||
@ -1916,24 +1736,16 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
void
|
||||
InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
|
||||
int64_t budget = sActiveIntersliceGCBudget;
|
||||
if (!aDeadline.IsNull()) {
|
||||
budget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
|
||||
}
|
||||
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
|
||||
nsJSContext::GarbageCollectNow(aData ?
|
||||
static_cast<JS::gcreason::Reason>(reason) :
|
||||
JS::gcreason::INTER_SLICE_GC,
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
int64_t budget = XRE_IsE10sParentProcess() && nsContentUtils::GetUserIsInteracting() && sActiveIntersliceGCBudget ?
|
||||
sActiveIntersliceGCBudget : NS_INTERSLICE_GC_BUDGET;
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
|
||||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::NonShrinkingGC,
|
||||
budget);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -1941,12 +1753,9 @@ void
|
||||
GCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsJSContext::KillGCTimer();
|
||||
// Now start the actual GC after initial timer has fired.
|
||||
sInterSliceGCRunner = CollectorRunner::Create(InterSliceGCRunnerFired,
|
||||
NS_INTERSLICE_GC_DELAY,
|
||||
sActiveIntersliceGCBudget,
|
||||
false,
|
||||
aClosure);
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
|
||||
nsJSContext::IncrementalGC);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -1969,11 +1778,11 @@ ShouldTriggerCC(uint32_t aSuspected)
|
||||
TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
|
||||
}
|
||||
|
||||
static bool
|
||||
CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
static void
|
||||
CCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t ccDelay = NS_CC_DELAY;
|
||||
@ -1982,53 +1791,48 @@ CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
|
||||
PRTime now = PR_Now();
|
||||
if (sCCLockedOutTime == 0) {
|
||||
// Reset sCCRunnerFireCount so that we run forgetSkippable
|
||||
// Reset sCCTimerFireCount so that we run forgetSkippable
|
||||
// often enough before CC. Because of reduced ccDelay
|
||||
// forgetSkippable will be called just a few times.
|
||||
// NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
|
||||
// forgetSkippable and CycleCollectNow eventually.
|
||||
sCCRunnerFireCount = 0;
|
||||
sCCTimerFireCount = 0;
|
||||
sCCLockedOutTime = now;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
++sCCRunnerFireCount;
|
||||
|
||||
bool didDoWork = false;
|
||||
++sCCTimerFireCount;
|
||||
|
||||
// During early timer fires, we only run forgetSkippable. During the first
|
||||
// late timer fire, we decide if we are going to have a second and final
|
||||
// late timer fire, where we may begin to run the CC. Should run at least one
|
||||
// early timer fire to allow cleanup before the CC.
|
||||
int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
|
||||
bool isLateTimerFire = sCCRunnerFireCount > numEarlyTimerFires;
|
||||
bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
|
||||
uint32_t suspected = nsCycleCollector_suspectedCount();
|
||||
if (isLateTimerFire && ShouldTriggerCC(suspected)) {
|
||||
if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
|
||||
if (sCCTimerFireCount == numEarlyTimerFires + 1) {
|
||||
FireForgetSkippable(suspected, true);
|
||||
didDoWork = true;
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
// Our efforts to avoid a CC have failed, so we return to let the
|
||||
// timer fire once more to trigger a CC.
|
||||
return didDoWork;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We are in the final timer fire and still meet the conditions for
|
||||
// triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
|
||||
// any because that will allow us to include the GC time in the CC pause.
|
||||
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||
didDoWork = true;
|
||||
nsJSContext::RunCycleCollectorSlice();
|
||||
}
|
||||
} else if (((sPreviousSuspectedCount + 100) <= suspected) ||
|
||||
(sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
|
||||
// Only do a forget skippable if there are more than a few new objects
|
||||
// or we're doing the initial forget skippables.
|
||||
FireForgetSkippable(suspected, false);
|
||||
didDoWork = true;
|
||||
}
|
||||
|
||||
if (isLateTimerFire) {
|
||||
@ -2037,10 +1841,8 @@ CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
// We have either just run the CC or decided we don't want to run the CC
|
||||
// next time, so kill the timer.
|
||||
sPreviousSuspectedCount = 0;
|
||||
nsJSContext::KillCCRunner();
|
||||
nsJSContext::KillCCTimer();
|
||||
}
|
||||
|
||||
return didDoWork;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -2087,13 +1889,13 @@ ReadyToTriggerExpensiveCollectorTimer()
|
||||
}
|
||||
|
||||
|
||||
// Check all of the various collector timers/runners and see if they are waiting to fire.
|
||||
// For the synchronous collector timers/runners, sGCTimer and sCCRunner, we only want to
|
||||
// trigger the collection occasionally, because they are expensive. The incremental collector
|
||||
// timers, sInterSliceGCRunner and sICCRunner, are fast and need to be run many times, so
|
||||
// Check all of the various collector timers and see if they are waiting to fire.
|
||||
// For the synchronous collector timers, sGCTimer and sCCTimer, we only want to trigger
|
||||
// the collection occasionally, because they are expensive. The incremental collector
|
||||
// timers, sInterSliceGCTimer and sICCTimer, are fast and need to be run many times, so
|
||||
// always run their corresponding timer.
|
||||
|
||||
// This does not check sFullGCTimer, as that's a more expensive collection we run
|
||||
// This does not check sFullGCTimer, as that's an even more expensive collection we run
|
||||
// on a long timer.
|
||||
|
||||
// static
|
||||
@ -2111,8 +1913,8 @@ nsJSContext::RunNextCollectorTimer()
|
||||
return;
|
||||
}
|
||||
|
||||
if (sInterSliceGCRunner) {
|
||||
InterSliceGCRunnerFired(TimeStamp(), nullptr);
|
||||
if (sInterSliceGCTimer) {
|
||||
InterSliceGCTimerFired(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2120,15 +1922,15 @@ nsJSContext::RunNextCollectorTimer()
|
||||
// anything if a GC is in progress.
|
||||
MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
|
||||
|
||||
if (sCCRunner) {
|
||||
if (sCCTimer) {
|
||||
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
||||
CCRunnerFired(TimeStamp(), nullptr);
|
||||
CCTimerFired(nullptr, nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (sICCRunner) {
|
||||
ICCRunnerFired(TimeStamp(), nullptr);
|
||||
if (sICCTimer) {
|
||||
ICCTimerFired(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2150,12 +1952,12 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||
sNeedsFullGC = true;
|
||||
}
|
||||
|
||||
if (sGCTimer || sInterSliceGCRunner) {
|
||||
if (sGCTimer || sInterSliceGCTimer) {
|
||||
// There's already a timer for GC'ing, just return
|
||||
return;
|
||||
}
|
||||
|
||||
if (sCCRunner) {
|
||||
if (sCCTimer) {
|
||||
// Make sure CC is called...
|
||||
sNeedsFullCC = true;
|
||||
// and GC after it.
|
||||
@ -2163,7 +1965,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||
return;
|
||||
}
|
||||
|
||||
if (sICCRunner) {
|
||||
if (sICCTimer) {
|
||||
// Make sure GC is called after the current CC completes.
|
||||
// No need to set sNeedsFullCC because we are currently running a CC.
|
||||
sNeedsGCAfterCC = true;
|
||||
@ -2189,7 +1991,6 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||
: NS_GC_DELAY),
|
||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
||||
"GCTimerFired");
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
@ -2219,19 +2020,24 @@ nsJSContext::PokeShrinkingGC()
|
||||
void
|
||||
nsJSContext::MaybePokeCC()
|
||||
{
|
||||
if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
|
||||
if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
sCCRunnerFireCount = 0;
|
||||
|
||||
sCCTimerFireCount = 0;
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
|
||||
if (!sCCTimer) {
|
||||
return;
|
||||
}
|
||||
// We can kill some objects before running forgetSkippable.
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
|
||||
sCCRunner =
|
||||
CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
|
||||
kForgetSkippableSliceDuration, true);
|
||||
sCCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
||||
NS_CC_SKIPPABLE_DELAY,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"CCTimerFired");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2255,11 +2061,11 @@ nsJSContext::KillFullGCTimer()
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::KillInterSliceGCRunner()
|
||||
nsJSContext::KillInterSliceGCTimer()
|
||||
{
|
||||
if (sInterSliceGCRunner) {
|
||||
sInterSliceGCRunner->Cancel();
|
||||
sInterSliceGCRunner = nullptr;
|
||||
if (sInterSliceGCTimer) {
|
||||
sInterSliceGCTimer->Cancel();
|
||||
NS_RELEASE(sInterSliceGCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2275,24 +2081,24 @@ nsJSContext::KillShrinkingGCTimer()
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillCCRunner()
|
||||
nsJSContext::KillCCTimer()
|
||||
{
|
||||
sCCLockedOutTime = 0;
|
||||
if (sCCRunner) {
|
||||
sCCRunner->Cancel();
|
||||
sCCRunner = nullptr;
|
||||
if (sCCTimer) {
|
||||
sCCTimer->Cancel();
|
||||
NS_RELEASE(sCCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillICCRunner()
|
||||
nsJSContext::KillICCTimer()
|
||||
{
|
||||
sCCLockedOutTime = 0;
|
||||
|
||||
if (sICCRunner) {
|
||||
sICCRunner->Cancel();
|
||||
sICCRunner = nullptr;
|
||||
if (sICCTimer) {
|
||||
sICCTimer->Cancel();
|
||||
NS_RELEASE(sICCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2364,8 +2170,8 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
||||
sCCLockedOut = false;
|
||||
sIsCompactingOnUserInactive = false;
|
||||
|
||||
// May need to kill the inter-slice GC runner
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
// May need to kill the inter-slice GC timer
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
|
||||
sCCollectedWaitingForGC = 0;
|
||||
sCCollectedZonesWaitingForGC = 0;
|
||||
@ -2406,11 +2212,15 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
||||
case JS::GC_SLICE_END:
|
||||
|
||||
// Schedule another GC slice if the GC has more work to do.
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
if (!sShuttingDown && !aDesc.isComplete_) {
|
||||
sInterSliceGCRunner =
|
||||
CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
|
||||
sActiveIntersliceGCBudget, false);
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
|
||||
sInterSliceGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired,
|
||||
nullptr,
|
||||
NS_INTERSLICE_GC_DELAY,
|
||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
||||
"InterSliceGCTimerFired");
|
||||
}
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
@ -2464,7 +2274,7 @@ void
|
||||
mozilla::dom::StartupJSEnvironment()
|
||||
{
|
||||
// initialize all our statics, so that we can restart XPCOM
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
||||
sCCLockedOut = false;
|
||||
sCCLockedOutTime = 0;
|
||||
sLastCCEndTime = TimeStamp();
|
||||
@ -2799,6 +2609,52 @@ nsJSContext::EnsureStatics()
|
||||
sIsInitialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::NotifyDidPaint()
|
||||
{
|
||||
sDidPaintAfterPreviousICCSlice = true;
|
||||
if (sICCTimer) {
|
||||
static uint32_t sCount = 0;
|
||||
// 16 here is the common value for refresh driver tick frequency.
|
||||
static const uint32_t kTicksPerSliceDelay = kICCIntersliceDelay / 16;
|
||||
if (++sCount % kTicksPerSliceDelay != 0) {
|
||||
// Don't trigger CC slice all the time after paint, but often still.
|
||||
// The key point is to trigger it right after paint, especially when
|
||||
// we're running RefreshDriver constantly.
|
||||
return;
|
||||
}
|
||||
|
||||
sICCTimer->Cancel();
|
||||
ICCTimerFired(nullptr, nullptr);
|
||||
if (sICCTimer) {
|
||||
sICCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
||||
kICCIntersliceDelay,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"ICCTimerFired");
|
||||
}
|
||||
} else if (sCCTimer) {
|
||||
static uint32_t sCount = 0;
|
||||
static const uint32_t kTicksPerForgetSkippableDelay =
|
||||
NS_CC_SKIPPABLE_DELAY / 16;
|
||||
if (++sCount % kTicksPerForgetSkippableDelay != 0) {
|
||||
// The comment above about triggering CC slice applies to forget skippable
|
||||
// too.
|
||||
return;
|
||||
}
|
||||
|
||||
sCCTimer->Cancel();
|
||||
CCTimerFired(nullptr, nullptr);
|
||||
if (sCCTimer) {
|
||||
sCCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
||||
NS_CC_SKIPPABLE_DELAY,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"CCTimerFired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsScriptNameSpaceManager*
|
||||
mozilla::dom::GetNameSpaceManager()
|
||||
{
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsIArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
@ -92,7 +91,7 @@ public:
|
||||
int32_t aExtraForgetSkippableCalls = 0);
|
||||
|
||||
// Run a cycle collector slice, using a heuristic to decide how long to run it.
|
||||
static void RunCycleCollectorSlice(mozilla::TimeStamp aDeadline);
|
||||
static void RunCycleCollectorSlice();
|
||||
|
||||
// Run a cycle collector slice, using the given work budget.
|
||||
static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
|
||||
@ -114,10 +113,10 @@ public:
|
||||
static void KillShrinkingGCTimer();
|
||||
|
||||
static void MaybePokeCC();
|
||||
static void KillCCRunner();
|
||||
static void KillICCRunner();
|
||||
static void KillCCTimer();
|
||||
static void KillICCTimer();
|
||||
static void KillFullGCTimer();
|
||||
static void KillInterSliceGCRunner();
|
||||
static void KillInterSliceGCTimer();
|
||||
|
||||
// Calling LikelyShortLivingObjectCreated() makes a GC more likely.
|
||||
static void LikelyShortLivingObjectCreated();
|
||||
@ -132,6 +131,7 @@ public:
|
||||
return global ? mGlobalObjectRef.get() : nullptr;
|
||||
}
|
||||
|
||||
static void NotifyDidPaint();
|
||||
protected:
|
||||
virtual ~nsJSContext();
|
||||
|
||||
|
@ -1697,46 +1697,6 @@ nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
|
||||
}
|
||||
}
|
||||
|
||||
struct RunnableWithDelay
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
uint32_t mDelay;
|
||||
};
|
||||
|
||||
static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
|
||||
|
||||
void
|
||||
nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
|
||||
uint32_t aDelay)
|
||||
{
|
||||
if (!sPendingIdleRunnables) {
|
||||
sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
|
||||
}
|
||||
|
||||
RunnableWithDelay rwd = {aRunnable, aDelay};
|
||||
sPendingIdleRunnables->AppendElement(rwd);
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
if (!sPendingIdleRunnables) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
|
||||
if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
|
||||
sPendingIdleRunnables->RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sPendingIdleRunnables->IsEmpty()) {
|
||||
delete sPendingIdleRunnables;
|
||||
sPendingIdleRunnables = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
{
|
||||
@ -1999,7 +1959,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
}
|
||||
}
|
||||
|
||||
bool dispatchRunnablesAfterTick = false;
|
||||
bool notifyGC = false;
|
||||
if (mViewManagerFlushIsPending) {
|
||||
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
||||
|
||||
@ -2038,7 +1998,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
|
||||
}
|
||||
|
||||
dispatchRunnablesAfterTick = true;
|
||||
notifyGC = true;
|
||||
}
|
||||
|
||||
#ifndef ANDROID /* bug 1142079 */
|
||||
@ -2057,14 +2017,10 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
ScheduleViewManagerFlush();
|
||||
}
|
||||
|
||||
if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
|
||||
AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
|
||||
sPendingIdleRunnables = nullptr;
|
||||
for (uint32_t i = 0; i < runnables->Length(); ++i) {
|
||||
NS_IdleDispatchToCurrentThread((*runnables)[i].mRunnable.forget(),
|
||||
(*runnables)[i].mDelay);
|
||||
}
|
||||
delete runnables;
|
||||
if (notifyGC && nsContentUtils::XPConnect()) {
|
||||
GeckoProfilerTracingRAII tracer("Paint", "NotifyDidPaint");
|
||||
nsContentUtils::XPConnect()->NotifyDidPaint();
|
||||
nsJSContext::NotifyDidPaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ class nsIDocument;
|
||||
class imgIRequest;
|
||||
class nsIDOMEvent;
|
||||
class nsINode;
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
class RefreshDriverTimer;
|
||||
@ -334,10 +333,6 @@ public:
|
||||
*/
|
||||
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
|
||||
|
||||
static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
|
||||
uint32_t aDelay);
|
||||
static void CancelIdleRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
bool SkippedPaints() const
|
||||
{
|
||||
return mSkippedPaints;
|
||||
|
@ -144,7 +144,7 @@ function StartTestURI(type, uri, timeout)
|
||||
// the JS ref tests disable the normal browser chrome and do not otherwise
|
||||
// create substatial DOM garbage, the CC tends not to run enough normally.
|
||||
++gTestCount;
|
||||
if (gTestCount % 250 == 0) {
|
||||
if (gTestCount % 1000 == 0) {
|
||||
CU.forceGC();
|
||||
CU.forceCC();
|
||||
}
|
||||
|
@ -1394,7 +1394,7 @@ pref("javascript.options.mem.high_water_mark", 128);
|
||||
pref("javascript.options.mem.max", -1);
|
||||
pref("javascript.options.mem.gc_per_zone", true);
|
||||
pref("javascript.options.mem.gc_incremental", true);
|
||||
pref("javascript.options.mem.gc_incremental_slice_ms", 5);
|
||||
pref("javascript.options.mem.gc_incremental_slice_ms", 10);
|
||||
pref("javascript.options.mem.gc_compacting", true);
|
||||
pref("javascript.options.mem.log", false);
|
||||
pref("javascript.options.mem.notify", false);
|
||||
|
Loading…
Reference in New Issue
Block a user