Bug 1084651 - Part 1: Thread SliceBudget through several functions to choose the budget type at the source. r=billm

This commit is contained in:
Emanuel Hoogeveen 2014-11-05 00:33:00 +01:00
parent c6102fe059
commit 8c558ad026
5 changed files with 58 additions and 55 deletions

View File

@ -628,16 +628,15 @@ GCSlice(JSContext *cx, unsigned argc, Value *vp)
return false; return false;
} }
bool limit = true; SliceBudget budget;
uint32_t budget = 0;
if (args.length() == 1) { if (args.length() == 1) {
if (!ToUint32(cx, args[0], &budget)) uint32_t work = 0;
if (!ToUint32(cx, args[0], &work))
return false; return false;
} else { budget = SliceBudget(SliceBudget::WorkBudget(work));
limit = false;
} }
cx->runtime()->gc.gcDebugSlice(limit, budget); cx->runtime()->gc.gcDebugSlice(budget);
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }

View File

@ -281,7 +281,7 @@ class GCRuntime
void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason); void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0); void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason); void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
void gcDebugSlice(bool limit, int64_t objCount); void gcDebugSlice(SliceBudget &budget);
void runDebugGC(); void runDebugGC();
inline void poke(); inline void poke();
@ -509,14 +509,14 @@ class GCRuntime
bool initZeal(); bool initZeal();
void requestMajorGC(JS::gcreason::Reason reason); void requestMajorGC(JS::gcreason::Reason reason);
void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind, void collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason); JS::gcreason::Reason reason);
bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind, bool gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason); JS::gcreason::Reason reason);
gcstats::ZoneGCStats scanZonesBeforeGC(); gcstats::ZoneGCStats scanZonesBeforeGC();
void budgetIncrementalGC(int64_t *budget); void budgetIncrementalGC(SliceBudget &budget);
void resetIncrementalGC(const char *reason); void resetIncrementalGC(const char *reason);
void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason); void incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason reason);
void pushZealSelectedObjects(); void pushZealSelectedObjects();
void purgeRuntime(); void purgeRuntime();
bool beginMarkPhase(JS::gcreason::Reason reason); bool beginMarkPhase(JS::gcreason::Reason reason);

View File

@ -87,12 +87,14 @@ BEGIN_TEST(testGCFinalizeCallback)
FinalizeCalls = 0; FinalizeCalls = 0;
JS_SetGCZeal(cx, 9, 1000000); JS_SetGCZeal(cx, 9, 1000000);
JS::PrepareForFullGC(rt); JS::PrepareForFullGC(rt);
rt->gc.gcDebugSlice(true, 1); js::SliceBudget budget(js::SliceBudget::WorkBudget(1));
rt->gc.gcDebugSlice(budget);
CHECK(rt->gc.state() == js::gc::MARK); CHECK(rt->gc.state() == js::gc::MARK);
CHECK(rt->gc.isFullGc()); CHECK(rt->gc.isFullGc());
JS::RootedObject global4(cx, createTestGlobal()); JS::RootedObject global4(cx, createTestGlobal());
rt->gc.gcDebugSlice(true, 1); budget = js::SliceBudget(js::SliceBudget::WorkBudget(1));
rt->gc.gcDebugSlice(budget);
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL); CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
CHECK(!rt->gc.isFullGc()); CHECK(!rt->gc.isFullGc());
CHECK(checkMultipleGroups()); CHECK(checkMultipleGroups());

View File

@ -89,7 +89,8 @@ BEGIN_TEST(testWeakMap_keyDelegates)
* zone to finish marking before the delegate zone. * zone to finish marking before the delegate zone.
*/ */
CHECK(newCCW(map, delegate)); CHECK(newCCW(map, delegate));
rt->gc.gcDebugSlice(true, 1000000); js::SliceBudget budget(js::SliceBudget::WorkBudget(1000000));
rt->gc.gcDebugSlice(budget);
#ifdef DEBUG #ifdef DEBUG
CHECK(map->zone()->lastZoneGroupIndex() < delegate->zone()->lastZoneGroupIndex()); CHECK(map->zone()->lastZoneGroupIndex() < delegate->zone()->lastZoneGroupIndex());
#endif #endif
@ -102,7 +103,8 @@ BEGIN_TEST(testWeakMap_keyDelegates)
/* Check the delegate keeps the entry alive even if the key is not reachable. */ /* Check the delegate keeps the entry alive even if the key is not reachable. */
key = nullptr; key = nullptr;
CHECK(newCCW(map, delegate)); CHECK(newCCW(map, delegate));
rt->gc.gcDebugSlice(true, 100000); budget = js::SliceBudget(js::SliceBudget::WorkBudget(100000));
rt->gc.gcDebugSlice(budget);
CHECK(checkSize(map, 1)); CHECK(checkSize(map, 1));
/* /*

View File

@ -1457,7 +1457,7 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
setMaxMallocBytes(value); setMaxMallocBytes(value);
break; break;
case JSGC_SLICE_TIME_BUDGET: case JSGC_SLICE_TIME_BUDGET:
sliceBudget = SliceBudget::TimeBudget(value); sliceBudget = value;
break; break;
case JSGC_MARK_STACK_LIMIT: case JSGC_MARK_STACK_LIMIT:
setMarkStackLimit(value); setMarkStackLimit(value);
@ -1554,7 +1554,7 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC &lock)
case JSGC_TOTAL_CHUNKS: case JSGC_TOTAL_CHUNKS:
return uint32_t(chunkSet.count() + emptyChunks(lock).count()); return uint32_t(chunkSet.count() + emptyChunks(lock).count());
case JSGC_SLICE_TIME_BUDGET: case JSGC_SLICE_TIME_BUDGET:
return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0); return uint32_t(sliceBudget > 0 ? sliceBudget : 0);
case JSGC_MARK_STACK_LIMIT: case JSGC_MARK_STACK_LIMIT:
return marker.maxCapacity(); return marker.maxCapacity();
case JSGC_HIGH_FREQUENCY_TIME_LIMIT: case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
@ -5533,7 +5533,7 @@ GCRuntime::resetIncrementalGC(const char *reason)
break; break;
} }
case SWEEP: case SWEEP: {
marker.reset(); marker.reset();
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
@ -5541,13 +5541,15 @@ GCRuntime::resetIncrementalGC(const char *reason)
/* Finish sweeping the current zone group, then abort. */ /* Finish sweeping the current zone group, then abort. */
abortSweepAfterCurrentGroup = true; abortSweepAfterCurrentGroup = true;
incrementalCollectSlice(SliceBudget::Unlimited, JS::gcreason::RESET); SliceBudget budget;
incrementalCollectSlice(budget, JS::gcreason::RESET);
{ {
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD); gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
rt->gc.waitBackgroundSweepOrAllocEnd(); rt->gc.waitBackgroundSweepOrAllocEnd();
} }
break; break;
}
default: default:
MOZ_CRASH("Invalid incremental GC state"); MOZ_CRASH("Invalid incremental GC state");
@ -5635,8 +5637,7 @@ GCRuntime::pushZealSelectedObjects()
} }
void void
GCRuntime::incrementalCollectSlice(int64_t budget, GCRuntime::incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason reason)
JS::gcreason::Reason reason)
{ {
MOZ_ASSERT(rt->currentThreadHasExclusiveAccess()); MOZ_ASSERT(rt->currentThreadHasExclusiveAccess());
@ -5649,7 +5650,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
int zeal = 0; int zeal = 0;
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
if (reason == JS::gcreason::DEBUG_GC && budget != SliceBudget::Unlimited) { if (reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited()) {
/* /*
* Do the incremental collection type specified by zeal mode if the * Do the incremental collection type specified by zeal mode if the
* collection was triggered by runDebugGC() and incremental GC has not * collection was triggered by runDebugGC() and incremental GC has not
@ -5660,18 +5661,16 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
#endif #endif
MOZ_ASSERT_IF(incrementalState != NO_INCREMENTAL, isIncremental); MOZ_ASSERT_IF(incrementalState != NO_INCREMENTAL, isIncremental);
isIncremental = budget != SliceBudget::Unlimited; isIncremental = !budget.isUnlimited();
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) { if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
/* /*
* Yields between slices occurs at predetermined points in these modes; * Yields between slices occurs at predetermined points in these modes;
* the budget is not used. * the budget is not used.
*/ */
budget = SliceBudget::Unlimited; budget.reset();
} }
SliceBudget sliceBudget(budget);
if (incrementalState == NO_INCREMENTAL) { if (incrementalState == NO_INCREMENTAL) {
incrementalState = MARK_ROOTS; incrementalState = MARK_ROOTS;
lastMarkSlice = false; lastMarkSlice = false;
@ -5701,11 +5700,11 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
case MARK: { case MARK: {
/* If we needed delayed marking for gray roots, then collect until done. */ /* If we needed delayed marking for gray roots, then collect until done. */
if (!marker.hasBufferedGrayRoots()) { if (!marker.hasBufferedGrayRoots()) {
sliceBudget.reset(); budget.reset();
isIncremental = false; isIncremental = false;
} }
bool finished = drainMarkStack(sliceBudget, gcstats::PHASE_MARK); bool finished = drainMarkStack(budget, gcstats::PHASE_MARK);
if (!finished) if (!finished)
break; break;
@ -5731,7 +5730,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
* now exhasted. * now exhasted.
*/ */
beginSweepPhase(lastGC); beginSweepPhase(lastGC);
if (sliceBudget.isOverBudget()) if (budget.isOverBudget())
break; break;
/* /*
@ -5745,7 +5744,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
} }
case SWEEP: { case SWEEP: {
bool finished = sweepPhase(sliceBudget); bool finished = sweepPhase(budget);
if (!finished) if (!finished)
break; break;
@ -5786,32 +5785,32 @@ gc::IsIncrementalGCSafe(JSRuntime *rt)
} }
void void
GCRuntime::budgetIncrementalGC(int64_t *budget) GCRuntime::budgetIncrementalGC(SliceBudget &budget)
{ {
IncrementalSafety safe = IsIncrementalGCSafe(rt); IncrementalSafety safe = IsIncrementalGCSafe(rt);
if (!safe) { if (!safe) {
resetIncrementalGC(safe.reason()); resetIncrementalGC(safe.reason());
*budget = SliceBudget::Unlimited; budget.reset();
stats.nonincremental(safe.reason()); stats.nonincremental(safe.reason());
return; return;
} }
if (mode != JSGC_MODE_INCREMENTAL) { if (mode != JSGC_MODE_INCREMENTAL) {
resetIncrementalGC("GC mode change"); resetIncrementalGC("GC mode change");
*budget = SliceBudget::Unlimited; budget.reset();
stats.nonincremental("GC mode"); stats.nonincremental("GC mode");
return; return;
} }
if (isTooMuchMalloc()) { if (isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited; budget.reset();
stats.nonincremental("malloc bytes trigger"); stats.nonincremental("malloc bytes trigger");
} }
bool reset = false; bool reset = false;
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) { if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
*budget = SliceBudget::Unlimited; budget.reset();
stats.nonincremental("allocation trigger"); stats.nonincremental("allocation trigger");
} }
@ -5822,7 +5821,7 @@ GCRuntime::budgetIncrementalGC(int64_t *budget)
} }
if (zone->isTooMuchMalloc()) { if (zone->isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited; budget.reset();
stats.nonincremental("malloc bytes trigger"); stats.nonincremental("malloc bytes trigger");
} }
} }
@ -5868,7 +5867,7 @@ struct AutoDisableStoreBuffer
* to run another cycle. * to run another cycle.
*/ */
MOZ_NEVER_INLINE bool MOZ_NEVER_INLINE bool
GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind, GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason) JS::gcreason::Reason reason)
{ {
minorGC(reason); minorGC(reason);
@ -5922,9 +5921,9 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
resetIncrementalGC("requested"); resetIncrementalGC("requested");
stats.nonincremental("requested"); stats.nonincremental("requested");
budget = SliceBudget::Unlimited; budget.reset();
} else { } else {
budgetIncrementalGC(&budget); budgetIncrementalGC(budget);
} }
/* The GC was reset, so we need a do-over. */ /* The GC was reset, so we need a do-over. */
@ -6017,7 +6016,7 @@ GCRuntime::scanZonesBeforeGC()
} }
void void
GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind, GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason) JS::gcreason::Reason reason)
{ {
/* GC shouldn't be running in parallel execution mode */ /* GC shouldn't be running in parallel execution mode */
@ -6042,7 +6041,7 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
return; return;
#endif #endif
MOZ_ASSERT_IF(!incremental || budget != SliceBudget::Unlimited, JSGC_INCREMENTAL); MOZ_ASSERT_IF(!incremental || !budget.isUnlimited(), JSGC_INCREMENTAL);
AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC || AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC ||
reason == JS::gcreason::DESTROY_RUNTIME); reason == JS::gcreason::DESTROY_RUNTIME);
@ -6109,21 +6108,22 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
void void
GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason) GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
{ {
collect(false, SliceBudget::Unlimited, gckind, reason); SliceBudget budget;
collect(false, budget, gckind, reason);
} }
void void
GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis) GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
{ {
int64_t budget; SliceBudget budget;
if (millis) if (millis)
budget = SliceBudget::TimeBudget(millis); budget = SliceBudget(SliceBudget::TimeBudget(millis));
else if (reason == JS::gcreason::ALLOC_TRIGGER) else if (reason == JS::gcreason::ALLOC_TRIGGER)
budget = sliceBudget; budget = SliceBudget(SliceBudget::TimeBudget(sliceBudget));
else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled()) else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER; budget = SliceBudget(SliceBudget::TimeBudget(sliceBudget * IGC_MARK_SLICE_MULTIPLIER));
else else
budget = sliceBudget; budget = SliceBudget(SliceBudget::TimeBudget(sliceBudget));
collect(true, budget, gckind, reason); collect(true, budget, gckind, reason);
} }
@ -6131,7 +6131,8 @@ GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64
void void
GCRuntime::gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason) GCRuntime::gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
{ {
collect(true, SliceBudget::Unlimited, gckind, reason); SliceBudget budget;
collect(true, budget, gckind, reason);
} }
void void
@ -6174,9 +6175,8 @@ ZonesSelected(JSRuntime *rt)
} }
void void
GCRuntime::gcDebugSlice(bool limit, int64_t objCount) GCRuntime::gcDebugSlice(SliceBudget &budget)
{ {
int64_t budget = limit ? SliceBudget::WorkBudget(objCount) : SliceBudget::Unlimited;
if (!ZonesSelected(rt)) { if (!ZonesSelected(rt)) {
if (JS::IsIncrementalGCInProgress(rt)) if (JS::IsIncrementalGCInProgress(rt))
JS::PrepareForIncrementalGC(rt); JS::PrepareForIncrementalGC(rt);
@ -6427,12 +6427,12 @@ GCRuntime::runDebugGC()
PrepareForDebugGC(rt); PrepareForDebugGC(rt);
SliceBudget budget;
if (type == ZealIncrementalRootsThenFinish || if (type == ZealIncrementalRootsThenFinish ||
type == ZealIncrementalMarkAllThenFinish || type == ZealIncrementalMarkAllThenFinish ||
type == ZealIncrementalMultipleSlices) type == ZealIncrementalMultipleSlices)
{ {
js::gc::State initialState = incrementalState; js::gc::State initialState = incrementalState;
int64_t budget;
if (type == ZealIncrementalMultipleSlices) { if (type == ZealIncrementalMultipleSlices) {
/* /*
* Start with a small slice limit and double it every slice. This * Start with a small slice limit and double it every slice. This
@ -6443,10 +6443,10 @@ GCRuntime::runDebugGC()
incrementalLimit = zealFrequency / 2; incrementalLimit = zealFrequency / 2;
else else
incrementalLimit *= 2; incrementalLimit *= 2;
budget = SliceBudget::WorkBudget(incrementalLimit); budget = SliceBudget(SliceBudget::WorkBudget(incrementalLimit));
} else { } else {
// This triggers incremental GC but is actually ignored by IncrementalMarkSlice. // This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
budget = SliceBudget::WorkBudget(1); budget = SliceBudget(SliceBudget::WorkBudget(1));
} }
collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC); collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
@ -6461,9 +6461,9 @@ GCRuntime::runDebugGC()
incrementalLimit = zealFrequency / 2; incrementalLimit = zealFrequency / 2;
} }
} else if (type == ZealCompactValue) { } else if (type == ZealCompactValue) {
collect(false, SliceBudget::Unlimited, GC_SHRINK, JS::gcreason::DEBUG_GC); collect(false, budget, GC_SHRINK, JS::gcreason::DEBUG_GC);
} else { } else {
collect(false, SliceBudget::Unlimited, GC_NORMAL, JS::gcreason::DEBUG_GC); collect(false, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
} }
#endif #endif