mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1117768 - Fix assertion in AutoStopVerifyingBarriers and add tests, r=terrence
--HG-- extra : rebase_source : 105c0170ed76a94a5042bbdb428f6d43054933b9
This commit is contained in:
parent
3d42c13c85
commit
e40c6c6f27
@ -118,7 +118,9 @@ class AutoStopVerifyingBarriers
|
||||
gcstats::Phase outer = gc->stats.currentPhase();
|
||||
if (outer != gcstats::PHASE_NONE)
|
||||
gc->stats.endPhase(outer);
|
||||
MOZ_ASSERT(gc->stats.currentPhase() == gcstats::PHASE_NONE);
|
||||
MOZ_ASSERT((gc->stats.currentPhase() == gcstats::PHASE_NONE) ||
|
||||
(gc->stats.currentPhase() == gcstats::PHASE_GC_BEGIN) ||
|
||||
(gc->stats.currentPhase() == gcstats::PHASE_GC_END));
|
||||
|
||||
if (restartPreVerifier)
|
||||
gc->startVerifyPreBarriers();
|
||||
|
@ -1066,8 +1066,8 @@ Statistics::beginPhase(Phase phase)
|
||||
//
|
||||
// Reuse this mechanism for managing PHASE_MUTATOR.
|
||||
if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) {
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases));
|
||||
suspendedPhases[suspendedPhaseNestingDepth++] = parent;
|
||||
MOZ_ASSERT(suspendedPhaseNestingDepth <= mozilla::ArrayLength(suspendedPhases));
|
||||
recordPhaseEnd(parent);
|
||||
parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
|
||||
}
|
||||
|
@ -195,6 +195,8 @@ struct Statistics
|
||||
return phaseNesting[phaseNestingDepth - 1];
|
||||
}
|
||||
|
||||
static const size_t MAX_NESTING = 20;
|
||||
|
||||
private:
|
||||
JSRuntime *runtime;
|
||||
|
||||
@ -257,7 +259,6 @@ struct Statistics
|
||||
int64_t maxPauseInInterval;
|
||||
|
||||
/* Phases that are currently on stack. */
|
||||
static const size_t MAX_NESTING = 8;
|
||||
Phase phaseNesting[MAX_NESTING];
|
||||
size_t phaseNestingDepth;
|
||||
size_t activeDagSlot;
|
||||
|
@ -1654,6 +1654,149 @@ Quit(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace gcCallback {
|
||||
|
||||
struct MajorGC {
|
||||
int32_t depth;
|
||||
int32_t phases;
|
||||
};
|
||||
|
||||
static void
|
||||
majorGC(JSRuntime *rt, JSGCStatus status, void *data)
|
||||
{
|
||||
auto info = static_cast<MajorGC*>(data);
|
||||
if (!(info->phases & (1 << status)))
|
||||
return;
|
||||
|
||||
if (info->depth > 0) {
|
||||
info->depth--;
|
||||
PrepareForFullGC(rt);
|
||||
JS::GCForReason(rt, GC_NORMAL, gcreason::API);
|
||||
info->depth++;
|
||||
}
|
||||
}
|
||||
|
||||
struct MinorGC {
|
||||
int32_t phases;
|
||||
bool active;
|
||||
};
|
||||
|
||||
static void
|
||||
minorGC(JSRuntime *rt, JSGCStatus status, void *data)
|
||||
{
|
||||
auto info = static_cast<MinorGC*>(data);
|
||||
if (!(info->phases & (1 << status)))
|
||||
return;
|
||||
|
||||
if (info->active) {
|
||||
info->active = false;
|
||||
rt->gc.evictNursery(gcreason::DEBUG_GC);
|
||||
info->active = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process global, should really be runtime-local. Also, the final one of these
|
||||
// is currently leaked, since they are only deleted when changing.
|
||||
MajorGC *prevMajorGC = nullptr;
|
||||
MinorGC *prevMinorGC = nullptr;
|
||||
|
||||
} /* namespace gcCallback */
|
||||
|
||||
static bool
|
||||
SetGCCallback(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 1) {
|
||||
JS_ReportError(cx, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject opts(cx);
|
||||
if (!JS_ValueToObject(cx, args[0], &opts))
|
||||
return false;
|
||||
|
||||
RootedValue v(cx);
|
||||
if (!JS_GetProperty(cx, opts, "action", &v))
|
||||
return false;
|
||||
|
||||
JSString *str = JS::ToString(cx, v);
|
||||
if (!str)
|
||||
return false;
|
||||
JSAutoByteString action(cx, str);
|
||||
if (!action)
|
||||
return false;
|
||||
|
||||
int32_t phases = 0;
|
||||
if ((strcmp(action.ptr(), "minorGC") == 0) || (strcmp(action.ptr(), "majorGC") == 0)) {
|
||||
if (!JS_GetProperty(cx, opts, "phases", &v))
|
||||
return false;
|
||||
if (v.isUndefined()) {
|
||||
phases = (1 << JSGC_END);
|
||||
} else {
|
||||
JSString *str = JS::ToString(cx, v);
|
||||
if (!str)
|
||||
return false;
|
||||
JSAutoByteString phasesStr(cx, str);
|
||||
if (!phasesStr)
|
||||
return false;
|
||||
|
||||
if (strcmp(phasesStr.ptr(), "begin") == 0)
|
||||
phases = (1 << JSGC_BEGIN);
|
||||
else if (strcmp(phasesStr.ptr(), "end") == 0)
|
||||
phases = (1 << JSGC_END);
|
||||
else if (strcmp(phasesStr.ptr(), "both") == 0)
|
||||
phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
|
||||
else {
|
||||
JS_ReportError(cx, "Invalid callback phase");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gcCallback::prevMajorGC) {
|
||||
JS_SetGCCallback(cx->runtime(), nullptr, nullptr);
|
||||
js_delete<gcCallback::MajorGC>(gcCallback::prevMajorGC);
|
||||
gcCallback::prevMajorGC = nullptr;
|
||||
}
|
||||
|
||||
if (gcCallback::prevMinorGC) {
|
||||
JS_SetGCCallback(cx->runtime(), nullptr, nullptr);
|
||||
js_delete<gcCallback::MinorGC>(gcCallback::prevMinorGC);
|
||||
gcCallback::prevMinorGC = nullptr;
|
||||
}
|
||||
|
||||
if (strcmp(action.ptr(), "minorGC") == 0) {
|
||||
auto info = js_new<gcCallback::MinorGC>();
|
||||
info->phases = phases;
|
||||
info->active = true;
|
||||
JS_SetGCCallback(cx->runtime(), gcCallback::minorGC, info);
|
||||
} else if (strcmp(action.ptr(), "majorGC") == 0) {
|
||||
if (!JS_GetProperty(cx, opts, "depth", &v))
|
||||
return false;
|
||||
int32_t depth = 1;
|
||||
if (!v.isUndefined()) {
|
||||
if (!ToInt32(cx, v, &depth))
|
||||
return false;
|
||||
}
|
||||
if (depth > int32_t(gcstats::Statistics::MAX_NESTING - 4)) {
|
||||
JS_ReportError(cx, "Nesting depth too large, would overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto info = js_new<gcCallback::MajorGC>();
|
||||
info->phases = phases;
|
||||
info->depth = depth;
|
||||
JS_SetGCCallback(cx->runtime(), gcCallback::majorGC, info);
|
||||
} else {
|
||||
JS_ReportError(cx, "Unknown GC callback action");
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
StartTimingMutator(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
@ -4189,6 +4332,12 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
" Throw if the first two arguments are not the same (both +0 or both -0,\n"
|
||||
" both NaN, or non-zero and ===)."),
|
||||
|
||||
JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
|
||||
"setGCCallback({action:\"...\", options...})",
|
||||
" Set the GC callback. action may be:\n"
|
||||
" 'minorGC' - run a nursery collection\n"
|
||||
" 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
|
||||
|
||||
JS_FN_HELP("startTimingMutator", StartTimingMutator, 0, 0,
|
||||
"startTimingMutator()",
|
||||
" Start accounting time to mutator vs GC."),
|
||||
|
60
js/src/tests/shell/gcstats.js
Normal file
60
js/src/tests/shell/gcstats.js
Normal file
@ -0,0 +1,60 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell||xulRuntime.OS=="WINNT")
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
function garbage() {
|
||||
var x;
|
||||
for (var i = 0; i < 100000; i++)
|
||||
x = { 'i': i };
|
||||
}
|
||||
|
||||
setGCCallback({
|
||||
action: "majorGC",
|
||||
depth: 1,
|
||||
phases: "both"
|
||||
});
|
||||
|
||||
gc();
|
||||
garbage();
|
||||
|
||||
setGCCallback({
|
||||
action: "majorGC",
|
||||
depth: 2,
|
||||
phases: "both"
|
||||
});
|
||||
|
||||
gc();
|
||||
garbage();
|
||||
|
||||
setGCCallback({
|
||||
action: "majorGC",
|
||||
depth: 10,
|
||||
phases: "begin"
|
||||
});
|
||||
|
||||
gc();
|
||||
garbage();
|
||||
|
||||
setGCCallback({
|
||||
action: "minorGC",
|
||||
phases: "both"
|
||||
});
|
||||
|
||||
gc();
|
||||
garbage();
|
||||
|
||||
var caught = false;
|
||||
try {
|
||||
setGCCallback({
|
||||
action: "majorGC",
|
||||
depth: 10000,
|
||||
phases: "begin"
|
||||
});
|
||||
} catch (e) {
|
||||
caught = ((""+e).indexOf("Nesting depth too large") >= 0);
|
||||
}
|
||||
|
||||
reportCompare(caught, true);
|
Loading…
Reference in New Issue
Block a user