author | Bill McCloskey <wmccloskey@mozilla.com> |
Mon, 12 Nov 2012 14:57:53 -0800 | |
changeset 113029 | a10481c78d8e4d4a179b25f4aab47c3dec9c2a4c |
parent 113026 | 5ea36ae3e9f00aa0107e50e7e518ddc8bbf75376 |
child 113030 | 866e9c7d656d7a95c564fbd24b40a6da0ef5d442 |
push id | 23848 |
push user | emorley@mozilla.com |
push date | Tue, 13 Nov 2012 16:29:34 +0000 |
treeherder | mozilla-central@d56d537a1843 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | luke |
bugs | 810560 |
milestone | 19.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
|
js/src/jsapi.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.h | file | annotate | diff | comparison | revisions | |
js/src/jsgc.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsgc.h | file | annotate | diff | comparison | revisions | |
js/src/jsgcinlines.h | file | annotate | diff | comparison | revisions | |
js/src/jswrapper.cpp | file | annotate | diff | comparison | revisions | |
js/src/jswrapper.h | file | annotate | diff | comparison | revisions | |
js/xpconnect/src/XPCWrappedNative.cpp | file | annotate | diff | comparison | revisions |
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -799,17 +799,17 @@ JSRuntime::JSRuntime(JSUseHelperThreads gcSweepPhase(0), gcSweepCompartmentIndex(0), gcSweepKindIndex(0), gcArenasAllocatedDuringSweep(NULL), gcInterFrameGC(0), gcSliceBudget(SliceBudget::Unlimited), gcIncrementalEnabled(true), gcExactScanningEnabled(true), - gcInTransplant(false), + gcManipulatingDeadCompartments(false), gcObjectsMarkedInDeadCompartments(0), gcPoke(false), heapState(Idle), #ifdef JS_GC_ZEAL gcZeal_(0), gcZealFrequency(0), gcNextScheduled(0), gcDeterministicOnly(false), @@ -1553,17 +1553,17 @@ JS_TransplantObject(JSContext *cx, JSObj { RootedObject origobj(cx, origobjArg); RootedObject target(cx, targetArg); AssertHeapIsIdle(cx); JS_ASSERT(origobj != target); JS_ASSERT(!IsCrossCompartmentWrapper(origobj)); JS_ASSERT(!IsCrossCompartmentWrapper(target)); - AutoTransplantGC agc(cx); + AutoMaybeTouchDeadCompartments agc(cx); JSCompartment *destination = target->compartment(); WrapperMap &map = destination->crossCompartmentWrappers; Value origv = ObjectValue(*origobj); JSObject *newIdentity; if (origobj->compartment() == destination) { // If the original object is in the same compartment as the @@ -1627,17 +1627,17 @@ js_TransplantObjectWithWrapper(JSContext JSObject *targetobjArg, JSObject *targetwrapperArg) { RootedObject origobj(cx, origobjArg); RootedObject origwrapper(cx, origwrapperArg); RootedObject targetobj(cx, targetobjArg); RootedObject targetwrapper(cx, targetwrapperArg); - AutoTransplantGC agc(cx); + AutoMaybeTouchDeadCompartments agc(cx); AssertHeapIsIdle(cx); JS_ASSERT(!IsCrossCompartmentWrapper(origobj)); JS_ASSERT(!IsCrossCompartmentWrapper(origwrapper)); JS_ASSERT(!IsCrossCompartmentWrapper(targetobj)); JS_ASSERT(!IsCrossCompartmentWrapper(targetwrapper)); JSObject *newWrapper;
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -714,19 +714,20 @@ struct JSRuntime : js::RuntimeFriendFiel * currently only used for dynamic root analysis. Exact scanning starts out * enabled, and is disabled if e4x has been used. */ bool gcExactScanningEnabled; /* * This is true if we are in the middle of a brain transplant (e.g., - * JS_TransplantObject). + * JS_TransplantObject) or some other operation that can manipulate + * dead compartments. */ - bool gcInTransplant; + bool gcManipulatingDeadCompartments; /* * This field is incremented each time we mark an object inside a * compartment with no incoming cross-compartment pointers. Typically if * this happens it signals that an incremental GC is marking too much * stuff. At various times we check this counter and, if it has changed, we * run an immediate, non-incremental GC to clean up the dead * compartments. This should happen very rarely.
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3420,17 +3420,17 @@ BeginMarkPhase(JSRuntime *rt) * JS_TransplantObject and the like. The code here ensures that we don't * regress. * * Note that there are certain cases where allocations or read barriers in * dead compartments are difficult to avoid. We detect such cases (via the * gcObjectsMarkedInDeadCompartment counter) and redo any ongoing GCs after * the JS_TransplantObject function has finished. This ensures that the dead * compartments will be cleaned up. See AutoMarkInDeadCompartment and - * AutoTransplantGC for details. + * AutoMaybeTouchDeadCompartments for details. */ /* Set the maybeAlive flag based on cross-compartment edges. */ for (CompartmentsIter c(rt); !c.done(); c.next()) { for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) { Cell *dst = e.front().key.wrapped; dst->compartment()->maybeAlive = true; } @@ -5878,33 +5878,42 @@ PurgeJITCaches(JSCompartment *c) if (script->hasIonScript()) script->ion->purgeCaches(c); #endif } #endif } -AutoTransplantGC::AutoTransplantGC(JSContext *cx) +AutoMaybeTouchDeadCompartments::AutoMaybeTouchDeadCompartments(JSContext *cx) : runtime(cx->runtime), markCount(runtime->gcObjectsMarkedInDeadCompartments), inIncremental(IsIncrementalGCInProgress(runtime)), - inTransplant(runtime->gcInTransplant) -{ - runtime->gcInTransplant = true; -} - -AutoTransplantGC::~AutoTransplantGC() + manipulatingDeadCompartments(runtime->gcManipulatingDeadCompartments) +{ + runtime->gcManipulatingDeadCompartments = true; +} + +AutoMaybeTouchDeadCompartments::AutoMaybeTouchDeadCompartments(JSObject *obj) + : runtime(obj->compartment()->rt), + markCount(runtime->gcObjectsMarkedInDeadCompartments), + inIncremental(IsIncrementalGCInProgress(runtime)), + manipulatingDeadCompartments(runtime->gcManipulatingDeadCompartments) +{ + runtime->gcManipulatingDeadCompartments = true; +} + +AutoMaybeTouchDeadCompartments::~AutoMaybeTouchDeadCompartments() { if (inIncremental && runtime->gcObjectsMarkedInDeadCompartments != markCount) { PrepareForFullGC(runtime); js::GC(runtime, GC_NORMAL, gcreason::TRANSPLANT); } - runtime->gcInTransplant = inTransplant; + runtime->gcManipulatingDeadCompartments = manipulatingDeadCompartments; } } /* namespace js */ JS_PUBLIC_API(void) JS_IterateCompartments(JSRuntime *rt, void *data, JSIterateCompartmentCallback compartmentCallback) {
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1168,35 +1168,11 @@ MaybeVerifyBarriers(JSContext *cx, bool #endif } /* namespace gc */ void PurgeJITCaches(JSCompartment *c); -/* - * This auto class should be used around any code that does brain - * transplants. Brain transplants can cause problems because they operate on all - * compartments, whether live or dead. A brain transplant can cause a formerly - * dead object to be "reanimated" by causing a read or write barrier to be - * invoked on it during the transplant. - * - * To work around this issue, we observe when mark bits are set on objects in - * dead compartments. If this happens during a brain transplant, we do a full, - * non-incremental GC at the end of the brain transplant. This will clean up any - * objects that were improperly marked. - */ -struct AutoTransplantGC -{ - AutoTransplantGC(JSContext *cx); - ~AutoTransplantGC(); - - private: - JSRuntime *runtime; - unsigned markCount; - bool inIncremental; - bool inTransplant; -}; - } /* namespace js */ #endif /* jsgc_h___ */
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -21,26 +21,26 @@ using JS::AssertCanGC; namespace js { struct Shape; /* * This auto class should be used around any code that might cause a mark bit to - * be set on an object in a dead compartment. See AutoTransplantGC for more - * details. + * be set on an object in a dead compartment. See AutoMaybeTouchDeadCompartments + * for more details. */ struct AutoMarkInDeadCompartment { AutoMarkInDeadCompartment(JSCompartment *comp) : compartment(comp), scheduled(comp->scheduledForDestruction) { - if (comp->rt->gcInTransplant && comp->scheduledForDestruction) { + if (comp->rt->gcManipulatingDeadCompartments && comp->scheduledForDestruction) { comp->rt->gcObjectsMarkedInDeadCompartments++; comp->scheduledForDestruction = false; } } ~AutoMarkInDeadCompartment() { compartment->scheduledForDestruction = scheduled; }
--- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -1159,17 +1159,17 @@ js::RemapAllWrappersForObject(JSContext return true; } JS_FRIEND_API(bool) js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter) { - AutoTransplantGC agc(cx); + AutoMaybeTouchDeadCompartments agc(cx); AutoWrapperVector toRecompute(cx); for (CompartmentsIter c(cx->runtime); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue;
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -290,11 +290,39 @@ RemapAllWrappersForObject(JSContext *cx, JSObject *newTarget); // API to recompute all cross-compartment wrappers whose source and target // match the given filters. JS_FRIEND_API(bool) RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, const CompartmentFilter &targetFilter); +/* + * This auto class should be used around any code, such as brain transplants, + * that may touch dead compartments. Brain transplants can cause problems + * because they operate on all compartments, whether live or dead. A brain + * transplant can cause a formerly dead object to be "reanimated" by causing a + * read or write barrier to be invoked on it during the transplant. In this way, + * a compartment becomes a zombie, kept alive by repeatedly consuming + * (transplanted) brains. + * + * To work around this issue, we observe when mark bits are set on objects in + * dead compartments. If this happens during a brain transplant, we do a full, + * non-incremental GC at the end of the brain transplant. This will clean up any + * objects that were improperly marked. + */ +struct JS_FRIEND_API(AutoMaybeTouchDeadCompartments) +{ + // The version that takes an object just uses it for its runtime. + AutoMaybeTouchDeadCompartments(JSContext *cx); + AutoMaybeTouchDeadCompartments(JSObject *obj); + ~AutoMaybeTouchDeadCompartments(); + + private: + JSRuntime *runtime; + unsigned markCount; + bool inIncremental; + bool manipulatingDeadCompartments; +}; + } /* namespace js */ #endif
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -505,16 +505,19 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo XPCMarkableJSVal newParentVal_markable(&newParentVal); AutoMarkingJSVal newParentVal_automarker(ccx, &newParentVal_markable); JSBool needsSOW = false; JSBool needsCOW = false; mozilla::Maybe<JSAutoCompartment> ac; if (sciWrapper.GetFlags().WantPreCreate()) { + // PreCreate may touch dead compartments. + js::AutoDeadCompartmentGC agc(parent); + JSObject* plannedParent = parent; nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx, parent, &parent); if (NS_FAILED(rv)) return rv; if (rv == NS_SUCCESS_CHROME_ACCESS_ONLY) needsSOW = true; @@ -1748,16 +1751,19 @@ XPCWrappedNative::RescueOrphans(XPCCallC // NB: We pass stopAtOuter=false during the unwrap because Location objects // are parented to outer window proxies. nsresult rv; JSObject *parentObj = js::GetObjectParent(mFlatJSObject); if (!parentObj) return NS_OK; // Global object. We're done. parentObj = js::UnwrapObject(parentObj, /* stopAtOuter = */ false); + // PreCreate may touch dead compartments. + js::AutoDeadCompartmentGC agc(parentobj); + // There's one little nasty twist here. For reasons described in bug 752764, // we nuke SOW-ed objects after transplanting them. This means that nodes // parented to an element (such as XUL elements), can end up with a nuked proxy // in the parent chain, depending on the order of fixup. Because the proxy is // nuked, we can't follow it anywhere. But we _can_ find the new wrapper for // the underlying native parent, which is exactly what PreCreate does. // So do that here. if (MOZ_UNLIKELY(JS_IsDeadWrapper(parentObj))) { @@ -3805,16 +3811,19 @@ ConstructSlimWrapper(XPCCallContext &ccx JSObject* parent = xpcScope->GetGlobalJSObject(); if (!flags.WantPreCreate()) { SLIM_LOG_NOT_CREATED(ccx, identityObj, "scriptable helper has no PreCreate hook"); return false; } + // PreCreate may touch dead compartments. + js::AutoDeadCompartmentGC agc(parent); + JSObject* plannedParent = parent; nsresult rv = classInfoHelper->PreCreate(identityObj, ccx, parent, &parent); if (rv != NS_SUCCESS_ALLOW_SLIM_WRAPPERS) { SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused"); return false; }