Bug 810560 - Fix scheduledForDestruction assertion (r=luke)

--HG--
extra : rebase_source : 6c25ad6b505e818d66f1caaea907c55355f7559a
This commit is contained in:
Bill McCloskey 2012-11-12 14:57:53 -08:00
parent 9744ea0e6b
commit c9da53d854
8 changed files with 62 additions and 39 deletions

View File

@ -804,7 +804,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
gcSliceBudget(SliceBudget::Unlimited),
gcIncrementalEnabled(true),
gcExactScanningEnabled(true),
gcInTransplant(false),
gcManipulatingDeadCompartments(false),
gcObjectsMarkedInDeadCompartments(0),
gcPoke(false),
heapState(Idle),
@ -1558,7 +1558,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
JS_ASSERT(!IsCrossCompartmentWrapper(origobj));
JS_ASSERT(!IsCrossCompartmentWrapper(target));
AutoTransplantGC agc(cx);
AutoMaybeTouchDeadCompartments agc(cx);
JSCompartment *destination = target->compartment();
WrapperMap &map = destination->crossCompartmentWrappers;
@ -1632,7 +1632,7 @@ js_TransplantObjectWithWrapper(JSContext *cx,
RootedObject targetobj(cx, targetobjArg);
RootedObject targetwrapper(cx, targetwrapperArg);
AutoTransplantGC agc(cx);
AutoMaybeTouchDeadCompartments agc(cx);
AssertHeapIsIdle(cx);
JS_ASSERT(!IsCrossCompartmentWrapper(origobj));

View File

@ -719,9 +719,10 @@ struct JSRuntime : js::RuntimeFriendFields
/*
* 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

View File

@ -3425,7 +3425,7 @@ BeginMarkPhase(JSRuntime *rt)
* 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. */
@ -5883,23 +5883,32 @@ PurgeJITCaches(JSCompartment *c)
#endif
}
AutoTransplantGC::AutoTransplantGC(JSContext *cx)
AutoMaybeTouchDeadCompartments::AutoMaybeTouchDeadCompartments(JSContext *cx)
: runtime(cx->runtime),
markCount(runtime->gcObjectsMarkedInDeadCompartments),
inIncremental(IsIncrementalGCInProgress(runtime)),
inTransplant(runtime->gcInTransplant)
manipulatingDeadCompartments(runtime->gcManipulatingDeadCompartments)
{
runtime->gcInTransplant = true;
runtime->gcManipulatingDeadCompartments = true;
}
AutoTransplantGC::~AutoTransplantGC()
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 */

View File

@ -1173,30 +1173,6 @@ MaybeVerifyBarriers(JSContext *cx, bool always = false)
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___ */

View File

@ -26,8 +26,8 @@ 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
{
@ -35,7 +35,7 @@ struct AutoMarkInDeadCompartment
: compartment(comp),
scheduled(comp->scheduledForDestruction)
{
if (comp->rt->gcInTransplant && comp->scheduledForDestruction) {
if (comp->rt->gcManipulatingDeadCompartments && comp->scheduledForDestruction) {
comp->rt->gcObjectsMarkedInDeadCompartments++;
comp->scheduledForDestruction = false;
}

View File

@ -1164,7 +1164,7 @@ JS_FRIEND_API(bool)
js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
const CompartmentFilter &targetFilter)
{
AutoTransplantGC agc(cx);
AutoMaybeTouchDeadCompartments agc(cx);
AutoWrapperVector toRecompute(cx);

View File

@ -295,6 +295,34 @@ 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

View File

@ -510,6 +510,9 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx,
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);
@ -1753,6 +1756,9 @@ XPCWrappedNative::RescueOrphans(XPCCallContext& ccx)
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
@ -3810,6 +3816,9 @@ ConstructSlimWrapper(XPCCallContext &ccx,
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) {