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:
Wes Kocher 2017-06-08 16:09:28 -07:00
parent a798ee4fc3
commit 43acd1b6e6
6 changed files with 170 additions and 363 deletions

View File

@ -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()
{

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);