author | Bill McCloskey <wmccloskey@mozilla.com> |
Tue, 03 Apr 2012 14:07:38 -0700 | |
changeset 91126 | 15a23c3923ff9752395c339165cdf113629608a4 |
parent 91125 | bc7f3c6766263a33daf4fe0817c9d7b5fb738014 |
child 91127 | a0a7af840b83b2e5567ced718fdd7d9af968c0e3 |
push id | 8033 |
push user | wmccloskey@mozilla.com |
push date | Thu, 05 Apr 2012 22:47:07 +0000 |
treeherder | mozilla-inbound@a0a7af840b83 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | igor |
bugs | 742570 |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -16,29 +16,40 @@ #include "methodjit/MethodJIT.h" using namespace js; using namespace JS; static JSBool GC(JSContext *cx, unsigned argc, jsval *vp) { - JSCompartment *comp = NULL; + /* + * If the first argument is 'compartment', we collect any compartments + * previously scheduled for GC via schedulegc. If the first argument is an + * object, we collect the object's compartment (any any other compartments + * scheduled for GC). Otherwise, we collect call compartments. + */ + JSBool compartment = false; if (argc == 1) { Value arg = vp[2]; - if (arg.isObject()) - comp = UnwrapObject(&arg.toObject())->compartment(); + if (arg.isString()) { + if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment)) + return false; + } else if (arg.isObject()) { + PrepareCompartmentForGC(UnwrapObject(&arg.toObject())->compartment()); + compartment = true; + } } #ifndef JS_MORE_DETERMINISTIC size_t preBytes = cx->runtime->gcBytes; #endif - if (comp) - PrepareCompartmentForGC(comp); + if (compartment) + PrepareForDebugGC(cx->runtime); else PrepareForFullGC(cx->runtime); GCForReason(cx, gcreason::API); char buf[256] = { '\0' }; #ifndef JS_MORE_DETERMINISTIC JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n", (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes); @@ -172,26 +183,34 @@ GCZeal(JSContext *cx, unsigned argc, jsv JS_SetGCZeal(cx, (uint8_t)zeal, frequency); *vp = JSVAL_VOID; return JS_TRUE; } static JSBool ScheduleGC(JSContext *cx, unsigned argc, jsval *vp) { - uint32_t count; - if (argc != 1) { ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments"); return JS_FALSE; } - if (!JS_ValueToECMAUint32(cx, vp[2], &count)) - return JS_FALSE; - JS_ScheduleGC(cx, count); + Value arg(vp[2]); + if (arg.isInt32()) { + /* Schedule a GC to happen after |arg| allocations. */ + JS_ScheduleGC(cx, arg.toInt32()); + } else if (arg.isObject()) { + /* Ensure that |comp| is collected during the next GC. */ + JSCompartment *comp = UnwrapObject(&arg.toObject())->compartment(); + PrepareCompartmentForGC(comp); + } else if (arg.isString()) { + /* This allows us to schedule atomsCompartment for GC. */ + PrepareCompartmentForGC(arg.toString()->compartment()); + } + *vp = JSVAL_VOID; return JS_TRUE; } static JSBool VerifyBarriers(JSContext *cx, unsigned argc, jsval *vp) { if (argc) { @@ -468,18 +487,20 @@ static JSBool Terminate(JSContext *cx, unsigned arg, jsval *vp) { JS_ClearPendingException(cx); return JS_FALSE; } static JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, -"gc([obj])", -" Run the garbage collector. When obj is given, GC only its compartment."), +"gc([obj] | 'compartment')", +" Run the garbage collector. When obj is given, GC only its compartment.\n" +" If 'compartment' is given, GC any compartments that were scheduled for\n" +" GC via schedulegc."), JS_FN_HELP("gcparam", GCParameter, 2, 0, "gcparam(name [, value])", " Wrapper for JS_[GS]etGCParameter. The name is either maxBytes,\n" " maxMallocBytes, gcBytes, gcNumber, or sliceTimeBudget."), JS_FN_HELP("countHeap", CountHeap, 0, 0, "countHeap([start[, kind]])", @@ -506,18 +527,19 @@ static JSFunctionSpecWithHelp TestingFun " 1: Collect when roots are added or removed\n" " 2: Collect when memory is allocated\n" " 3: Collect when the window paints (browser only)\n" " 4: Verify write barriers between instructions\n" " 5: Verify write barriers between paints\n" " Period specifies that collection happens every n allocations.\n"), JS_FN_HELP("schedulegc", ScheduleGC, 1, 0, -"schedulegc(num)", -" Schedule a GC to happen after num allocations."), +"schedulegc(num | obj)", +" If num is given, schedule a GC after num allocations.\n" +" If obj is given, schedule a GC of obj's compartment."), JS_FN_HELP("verifybarriers", VerifyBarriers, 0, 0, "verifybarriers()", " Start or end a run of the write barrier verifier."), JS_FN_HELP("gcslice", GCSlice, 1, 0, "gcslice(n)", " Run an incremental GC slice that marks about n objects."),
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/gc/multi-01.js @@ -0,0 +1,9 @@ +/* Make sure we don't collect the atoms compartment unless every compartment is marked. */ + +var g = newGlobal('new-compartment'); +g.eval("var x = 'some-atom';"); + +schedulegc(this); +schedulegc('atoms'); +gc('compartment'); +print(g.x);
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3721,20 +3721,32 @@ void GCSlice(JSContext *cx, JSGCInvocationKind gckind, gcreason::Reason reason) { Collect(cx, cx->runtime->gcSliceBudget, gckind, reason); } void GCDebugSlice(JSContext *cx, int64_t objCount) { - PrepareForFullGC(cx->runtime); + PrepareForDebugGC(cx->runtime); Collect(cx, SliceBudget::WorkBudget(objCount), GC_NORMAL, gcreason::API); } +/* Schedule a full GC unless a compartment will already be collected. */ +void +PrepareForDebugGC(JSRuntime *rt) +{ + for (CompartmentsIter c(rt); !c.done(); c.next()) { + if (c->isGCScheduled()) + return; + } + + PrepareForFullGC(rt); +} + void ShrinkGCBuffers(JSRuntime *rt) { AutoLockGC lock(rt); JS_ASSERT(!rt->gcRunning); #ifndef JS_THREADSAFE ExpireChunksAndArenas(rt, true); #else @@ -3914,17 +3926,17 @@ NewCompartment(JSContext *cx, JSPrincipa Foreground::delete_(compartment); return NULL; } void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - PrepareForFullGC(cx->runtime); + PrepareForDebugGC(cx->runtime); RunLastDitchGC(cx, gcreason::DEBUG_GC); #endif } void SetDeterministicGC(JSContext *cx, bool enabled) { #ifdef JS_GC_ZEAL
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1401,16 +1401,19 @@ extern void GC(JSContext *cx, JSGCInvocationKind gckind, js::gcreason::Reason reason); extern void GCSlice(JSContext *cx, JSGCInvocationKind gckind, js::gcreason::Reason reason); extern void GCDebugSlice(JSContext *cx, int64_t objCount); +extern void +PrepareForDebugGC(JSRuntime *rt); + } /* namespace js */ namespace js { void InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback); #ifdef JS_THREADSAFE