Sweep compartments when no objects live in them (bug 639270, r=gregor).

This commit is contained in:
Andreas Gal 2011-03-08 18:10:47 -08:00
parent b55a8920aa
commit 74d162d185
6 changed files with 34 additions and 61 deletions

View File

@ -1333,7 +1333,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
// types of security wrappers based on whether the new compartment
// is same origin with them.
Value targetv = ObjectValue(*obj);
WrapperVector &vector = cx->runtime->compartments;
CompartmentVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
if (!toTransplant.reserve(vector.length()))
return NULL;
@ -1427,7 +1427,7 @@ js_TransplantObjectWithWrapper(JSContext *cx,
// and not |origwrapper|. They need to be updated to point at the new
// location object.
Value targetv = ObjectValue(*targetobj);
WrapperVector &vector = cx->runtime->compartments;
CompartmentVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
if (!toTransplant.reserve(vector.length()))
return NULL;
@ -3081,6 +3081,21 @@ JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
return obj;
}
class AutoHoldCompartment {
public:
AutoHoldCompartment(JSCompartment *compartment JS_GUARD_OBJECT_NOTIFIER_PARAM) {
JS_GUARD_OBJECT_NOTIFIER_INIT;
*(holdp = &compartment->hold) = true;
}
~AutoHoldCompartment() {
*holdp = false;
}
private:
bool *holdp;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
JS_PUBLIC_API(JSObject *)
JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals)
{
@ -3089,6 +3104,8 @@ JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *pr
if (!compartment)
return NULL;
AutoHoldCompartment hold(compartment);
JSCompartment *saved = cx->compartment;
cx->compartment = compartment;
JSObject *obj = JS_NewGlobalObject(cx, clasp);

View File

@ -988,7 +988,7 @@ typedef void
namespace js {
typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> WrapperVector;
typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> CompartmentVector;
}
@ -1000,7 +1000,7 @@ struct JSRuntime {
#endif
/* List of compartments (protected by the GC lock). */
js::WrapperVector compartments;
js::CompartmentVector compartments;
/* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
JSRuntimeState state;

View File

@ -66,6 +66,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
gcBytes(0),
gcTriggerBytes(0),
gcLastBytes(0),
hold(false),
data(NULL),
active(false),
#ifdef JS_METHODJIT
@ -82,8 +83,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
#if ENABLE_YARR_JIT
regExpAllocator(NULL),
#endif
mathCache(NULL),
marked(false)
mathCache(NULL)
{
JS_INIT_CLIST(&scripts);
@ -447,35 +447,23 @@ ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
/*
* This method marks pointers that cross compartment boundaries. It should be
* called only by per-compartment GCs, since full GCs naturally follow pointers
* called only for per-compartment GCs, since full GCs naturally follow pointers
* across compartments.
*/
void
JSCompartment::markCrossCompartment(JSTracer *trc)
JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
{
JS_ASSERT(trc->context->runtime->gcCurrentCompartment);
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
MarkValue(trc, e.front().key, "cross-compartment wrapper");
}
void
JSCompartment::mark(JSTracer *trc)
{
if (IS_GC_MARKING_TRACER(trc)) {
JSRuntime *rt = trc->context->runtime;
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != this)
return;
if (marked)
return;
marked = true;
}
}
void
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
{
chunk = NULL;
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&

View File

@ -381,6 +381,8 @@ struct JS_FRIEND_API(JSCompartment) {
size_t gcTriggerBytes;
size_t gcLastBytes;
bool hold;
#ifdef JS_GCMETER
js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT];
#endif
@ -451,10 +453,7 @@ struct JS_FRIEND_API(JSCompartment) {
bool init();
/* Mark cross-compartment wrappers. */
void markCrossCompartment(JSTracer *trc);
/* Mark this compartment's local roots. */
void mark(JSTracer *trc);
void markCrossCompartmentWrappers(JSTracer *trc);
bool wrap(JSContext *cx, js::Value *vp);
bool wrap(JSContext *cx, JSString **strp);
@ -481,8 +480,6 @@ struct JS_FRIEND_API(JSCompartment) {
js::MathCache *allocMathCache(JSContext *cx);
bool marked;
typedef js::HashMap<jsbytecode*,
size_t,
js::DefaultHasher<jsbytecode*>,
@ -496,9 +493,6 @@ struct JS_FRIEND_API(JSCompartment) {
return mathCache ? mathCache : allocMathCache(cx);
}
bool isMarked() { return marked; }
void clearMark() { marked = false; }
size_t backEdgeCount(jsbytecode *pc) const;
size_t incBackEdgeCount(jsbytecode *pc);
};

View File

@ -1633,9 +1633,6 @@ MarkContext(JSTracer *trc, JSContext *acx)
js_TraceSharpMap(trc, &acx->sharpObjectMap);
MarkValue(trc, acx->iterValue, "iterValue");
if (acx->compartment)
acx->compartment->mark(trc);
}
JS_REQUIRES_STACK void
@ -1724,8 +1721,6 @@ MarkRuntime(JSTracer *trc)
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
MarkContext(trc, acx);
rt->atomsCompartment->mark(trc);
#ifdef JS_TRACER
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->traceMonitor.mark(trc);
@ -2187,13 +2182,8 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
while (read < end) {
JSCompartment *compartment = *read++;
/*
* Unmarked compartments containing marked objects don't get deleted,
* except when LAST_CONTEXT GC is performed.
*/
if ((!compartment->isMarked() && compartment->arenaListsAreEmpty()) ||
gckind == GC_LAST_CONTEXT)
{
if (!compartment->hold &&
(compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) {
JS_ASSERT(compartment->freeLists.isEmpty());
if (callback)
(void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
@ -2268,7 +2258,6 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
JS_ASSERT(!rt->gcRegenShapes);
JS_ASSERT(gckind != GC_LAST_CONTEXT);
JS_ASSERT(comp != rt->atomsCompartment);
JS_ASSERT(!comp->isMarked());
JS_ASSERT(comp->rt->gcMode == JSGC_MODE_COMPARTMENT);
/*
@ -2284,9 +2273,7 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
r.front()->clearMarkBitmap();
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->markCrossCompartment(&gcmarker);
comp->mark(&gcmarker);
(*c)->markCrossCompartmentWrappers(&gcmarker);
MarkRuntime(&gcmarker);
@ -2384,8 +2371,6 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
ExpireGCChunks(rt);
TIMESTAMP(sweepDestroyEnd);
comp->clearMark();
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
}
@ -2523,9 +2508,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
ExpireGCChunks(rt);
TIMESTAMP(sweepDestroyEnd);
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->clearMark();
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
@ -2736,9 +2718,6 @@ GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
AutoGCSession gcsession(cx);
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
JS_ASSERT(!(*c)->isMarked());
/*
* We should not be depending on cx->compartment in the GC, so set it to
* NULL to look for violations.

View File

@ -6509,11 +6509,6 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
obj->trace(trc);
if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
JSCompartment *compartment = obj->getCompartment();
compartment->mark(trc);
}
/*
* NB: clasp->mark could mutate something (which would be a bug, but we are
* defensive), so don't hoist this above calling clasp->mark.