mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1103957 - prevent phase nesting, r=terrence
This commit is contained in:
parent
fd6d2cd524
commit
90fd4d6a3f
@ -283,6 +283,13 @@ struct PhaseInfo
|
||||
|
||||
static const Phase PHASE_NO_PARENT = PHASE_LIMIT;
|
||||
|
||||
/*
|
||||
* Note that PHASE_MUTATOR, PHASE_GC_BEGIN, and PHASE_GC_END never have any
|
||||
* child phases. If beginPhase is called while one of these is active, they
|
||||
* will automatically be suspended and resumed when the phase stack is next
|
||||
* empty. Timings for these phases are thus exclusive of any other phase.
|
||||
*/
|
||||
|
||||
static const PhaseInfo phases[] = {
|
||||
{ PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT },
|
||||
{ PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT },
|
||||
@ -639,11 +646,11 @@ Statistics::Statistics(JSRuntime *rt)
|
||||
fullFormat(false),
|
||||
gcDepth(0),
|
||||
nonincrementalReason(nullptr),
|
||||
timingMutator(false),
|
||||
timedGCStart(0),
|
||||
preBytes(0),
|
||||
maxPauseInInterval(0),
|
||||
phaseNestingDepth(0),
|
||||
suspendedPhaseNestingDepth(0),
|
||||
sliceCallback(nullptr)
|
||||
{
|
||||
PodArrayZero(phaseTotals);
|
||||
@ -833,12 +840,10 @@ Statistics::endSlice()
|
||||
void
|
||||
Statistics::startTimingMutator()
|
||||
{
|
||||
MOZ_ASSERT(!timingMutator);
|
||||
|
||||
// Should only be called from outside of GC
|
||||
MOZ_ASSERT(phaseNestingDepth == 0);
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth == 0);
|
||||
|
||||
timingMutator = true;
|
||||
timedGCTime = 0;
|
||||
phaseStartTimes[PHASE_MUTATOR] = 0;
|
||||
phaseTimes[PHASE_MUTATOR] = 0;
|
||||
@ -847,38 +852,42 @@ Statistics::startTimingMutator()
|
||||
beginPhase(PHASE_MUTATOR);
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
Statistics::stopTimingMutator(double &mutator_ms, double &gc_ms)
|
||||
{
|
||||
MOZ_ASSERT(timingMutator);
|
||||
|
||||
// Should only be called from outside of GC
|
||||
MOZ_ASSERT(phaseNestingDepth == 1 && phaseNesting[0] == PHASE_MUTATOR);
|
||||
// This should only be called from outside of GC, while timing the mutator.
|
||||
if (phaseNestingDepth != 1 || phaseNesting[0] != PHASE_MUTATOR)
|
||||
return false;
|
||||
|
||||
endPhase(PHASE_MUTATOR);
|
||||
mutator_ms = t(phaseTimes[PHASE_MUTATOR]);
|
||||
gc_ms = t(timedGCTime);
|
||||
timingMutator = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::beginPhase(Phase phase)
|
||||
{
|
||||
/* Guard against re-entry */
|
||||
MOZ_ASSERT(!phaseStartTimes[phase]);
|
||||
Phase parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
|
||||
|
||||
if (timingMutator) {
|
||||
if (phaseNestingDepth == 1 && phaseNesting[0] == PHASE_MUTATOR) {
|
||||
endPhase(PHASE_MUTATOR);
|
||||
timedGCStart = PRMJ_Now();
|
||||
}
|
||||
// Re-entry is allowed during callbacks. Do not account nested GC time
|
||||
// against the callbacks.
|
||||
//
|
||||
// Reuse this mechanism for managing PHASE_MUTATOR.
|
||||
if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) {
|
||||
suspendedPhases[suspendedPhaseNestingDepth++] = parent;
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth <= mozilla::ArrayLength(suspendedPhases));
|
||||
endPhase(parent);
|
||||
parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
|
||||
}
|
||||
|
||||
// Guard against any other re-entry.
|
||||
MOZ_ASSERT(!phaseStartTimes[phase]);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(phases[phase].index == phase);
|
||||
Phase parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
|
||||
MOZ_ASSERT(phaseNestingDepth < MAX_NESTING);
|
||||
// Major and minor GCs can nest inside PHASE_GC_BEGIN/PHASE_GC_END.
|
||||
MOZ_ASSERT_IF(gcDepth == 1 && phase != PHASE_MINOR_GC, phases[phase].parent == parent);
|
||||
#endif
|
||||
|
||||
@ -891,20 +900,31 @@ Statistics::beginPhase(Phase phase)
|
||||
void
|
||||
Statistics::endPhase(Phase phase)
|
||||
{
|
||||
int64_t now = PRMJ_Now();
|
||||
|
||||
if (phase == PHASE_MUTATOR)
|
||||
timedGCStart = now;
|
||||
|
||||
phaseNestingDepth--;
|
||||
|
||||
int64_t now = PRMJ_Now();
|
||||
int64_t t = now - phaseStartTimes[phase];
|
||||
if (!slices.empty())
|
||||
slices.back().phaseTimes[phase] += t;
|
||||
phaseTimes[phase] += t;
|
||||
phaseStartTimes[phase] = 0;
|
||||
|
||||
if (timingMutator) {
|
||||
if (phaseNestingDepth == 0 && phase != PHASE_MUTATOR) {
|
||||
// When emptying the stack, we may need to resume a callback phase
|
||||
// (PHASE_GC_BEGIN/END) or if not, return to timing the mutator
|
||||
// (PHASE_MUTATOR).
|
||||
//
|
||||
// However, if the phase we're ending is PHASE_MUTATOR, that means
|
||||
// beginPhase is calling endPhase(PHASE_MUTATOR) because some other phase
|
||||
// is starting. So don't resume any earlier phase.
|
||||
if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0 && phase != PHASE_MUTATOR) {
|
||||
Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth];
|
||||
if (resumePhase == PHASE_MUTATOR)
|
||||
timedGCTime += now - timedGCStart;
|
||||
beginPhase(PHASE_MUTATOR);
|
||||
}
|
||||
beginPhase(resumePhase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ struct Statistics
|
||||
void endSlice();
|
||||
|
||||
void startTimingMutator();
|
||||
void stopTimingMutator(double &mutator_ms, double &gc_ms);
|
||||
bool stopTimingMutator(double &mutator_ms, double &gc_ms);
|
||||
|
||||
void reset(const char *reason) { slices.back().resetReason = reason; }
|
||||
void nonincremental(const char *reason) { nonincrementalReason = reason; }
|
||||
@ -188,9 +188,6 @@ struct Statistics
|
||||
/* Most recent time when the given phase started. */
|
||||
int64_t phaseStartTimes[PHASE_LIMIT];
|
||||
|
||||
/* Are we currently timing mutator vs GC time? */
|
||||
bool timingMutator;
|
||||
|
||||
/* Bookkeeping for GC timings when timingMutator is true */
|
||||
int64_t timedGCStart;
|
||||
int64_t timedGCTime;
|
||||
@ -215,6 +212,15 @@ struct Statistics
|
||||
Phase phaseNesting[MAX_NESTING];
|
||||
size_t phaseNestingDepth;
|
||||
|
||||
/*
|
||||
* To avoid recursive nesting, we discontinue a callback phase when any
|
||||
* other phases are started. Remember what phase to resume when the inner
|
||||
* phases are complete. (And because GCs can nest within the callbacks any
|
||||
* number of times, we need a whole stack of of phases to resume.)
|
||||
*/
|
||||
Phase suspendedPhases[MAX_NESTING];
|
||||
size_t suspendedPhaseNestingDepth;
|
||||
|
||||
/* Sweep times for SCCs of compartments. */
|
||||
Vector<int64_t, 0, SystemAllocPolicy> sccTimes;
|
||||
|
||||
|
@ -1680,7 +1680,10 @@ StopTimingMutator(JSContext *cx, unsigned argc, jsval *vp)
|
||||
}
|
||||
|
||||
double mutator_ms, gc_ms;
|
||||
cx->runtime()->gc.stats.stopTimingMutator(mutator_ms, gc_ms);
|
||||
if (!cx->runtime()->gc.stats.stopTimingMutator(mutator_ms, gc_ms)) {
|
||||
JS_ReportError(cx, "stopTimingMutator called when not timing the mutator");
|
||||
return false;
|
||||
}
|
||||
double total_ms = mutator_ms + gc_ms;
|
||||
if (total_ms > 0) {
|
||||
fprintf(gOutFile, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
|
||||
|
Loading…
Reference in New Issue
Block a user