Bug 650161 - Fix up XPCJSRuntime object pointers on moving GC r=bholley

This commit is contained in:
Jon Coppeard 2014-09-18 18:14:50 +01:00
Родитель 3bbcf48581
Коммит 9cf17436a8
8 изменённых файлов: 136 добавлений и 36 удалений

Просмотреть файл

@ -491,6 +491,14 @@ void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj)
mJSObject = JSObj;
}
inline
void XPCWrappedNativeTearOff::JSObjectMoved(JSObject *obj, const JSObject *old)
{
MOZ_ASSERT(!IsMarked());
MOZ_ASSERT(mJSObject == old);
mJSObject = obj;
}
inline
XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff()
{

Просмотреть файл

@ -789,19 +789,16 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop,
MOZ_ASSERT(!self->mGCIsRunning, "bad state");
self->mGCIsRunning = true;
nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray =
&self->mWrappedJSToReleaseArray;
// Add any wrappers whose JSObjects are to be finalized to
// this array. Note that we do not want to be changing the
// refcount of these wrappers.
// We add them to the array now and Release the array members
// later to avoid the posibility of doing any JS GCThing
// allocations during the gc cycle.
self->mWrappedJSMap->FindDyingJSObjects(dyingWrappedJSArray);
self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
// Find dying scopes.
XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
self->mDoingFinalization = true;
break;
@ -816,7 +813,7 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop,
DoDeferredRelease(self->mWrappedJSToReleaseArray);
// Sweep scopes needing cleanup
XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
XPCWrappedNativeScope::KillDyingScopes();
MOZ_ASSERT(self->mGCIsRunning, "bad state");
self->mGCIsRunning = false;
@ -971,6 +968,19 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop,
}
}
/* static */ void
XPCJSRuntime::MovingGCCallback(JSRuntime *rt, void *data)
{
// Called to fixup any weak GC thing pointers that may have been moved.
XPCJSRuntime *self = static_cast<XPCJSRuntime *>(data);
self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
MOZ_ASSERT(self->WrappedJSToReleaseArray().IsEmpty());
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
}
static void WatchdogMain(void *arg);
class Watchdog;
class WatchdogManager;
@ -1537,6 +1547,7 @@ XPCJSRuntime::~XPCJSRuntime()
// callback if we aren't careful. Null out the relevant callbacks.
js::SetActivityCallback(Runtime(), nullptr, nullptr);
JS_RemoveFinalizeCallback(Runtime(), FinalizeCallback);
JS_RemoveMovingGCCallback(Runtime(), MovingGCCallback);
// Clear any pending exception. It might be an XPCWrappedJS, and if we try
// to destroy it later we will crash.
@ -3235,6 +3246,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
JS_AddFinalizeCallback(runtime, FinalizeCallback, nullptr);
JS_AddMovingGCCallback(runtime, MovingGCCallback, this);
JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
#ifdef MOZ_CRASHREPORTER

Просмотреть файл

@ -85,18 +85,44 @@ HashNativeKey(PLDHashTable *table, const void *key)
// implement JSObject2WrappedJSMap...
void
JSObject2WrappedJSMap::FindDyingJSObjects(nsTArray<nsXPCWrappedJS*>* dying)
JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime *runtime)
{
for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) {
nsXPCWrappedJS* wrapper = r.front().value();
// Check all wrappers and update their JSObject pointer if it has been
// moved, or queue the wrapper for destruction if it is about to be
// finalized.
nsTArray<nsXPCWrappedJS*> &dying = runtime->WrappedJSToReleaseArray();
MOZ_ASSERT(dying.IsEmpty());
for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
nsXPCWrappedJS* wrapper = e.front().value();
MOZ_ASSERT(wrapper, "found a null JS wrapper!");
// walk the wrapper chain and find any whose JSObject is to be finalized
// Walk the wrapper chain and update all JSObjects.
while (wrapper) {
#ifdef DEBUG
if (!wrapper->IsSubjectToFinalization()) {
JSObject *obj = wrapper->GetJSObjectPreserveColor();
JSObject *prior = obj;
MOZ_ASSERT(!JS_IsAboutToBeFinalizedUnbarriered(&obj));
// If a wrapper is not subject to finalization then it roots its
// JS object. If so, then any necessary pointer update will
// have already happened when it was marked.
MOZ_ASSERT(obj == prior);
}
#endif
if (wrapper->IsSubjectToFinalization() && wrapper->IsObjectAboutToBeFinalized())
dying->AppendElement(wrapper);
dying.AppendElement(wrapper);
wrapper = wrapper->GetNextWrapper();
}
// Remove or update the JSObject key in the table if necessary.
JSObject *obj = e.front().key();
JSObject *prior = obj;
if (JS_IsAboutToBeFinalizedUnbarriered(&obj))
e.removeFront();
else if (obj != prior)
e.rekeyFront(obj);
}
}

Просмотреть файл

@ -68,7 +68,7 @@ public:
r.front().value()->DebugDump(depth);
}
void FindDyingJSObjects(nsTArray<nsXPCWrappedJS*>* dying);
void UpdateWeakPointersAfterGC(XPCJSRuntime *runtime);
void ShutdownMarker();

Просмотреть файл

@ -1334,6 +1334,15 @@ XPC_WN_Shared_Proto_Finalize(js::FreeOp *fop, JSObject *obj)
p->JSProtoObjectFinalized(fop, obj);
}
static void
XPC_WN_Shared_Proto_ObjectMoved(JSObject *obj, const JSObject *old)
{
// This can be null if xpc shutdown has already happened
XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
if (p)
p->JSProtoObjectMoved(obj, old);
}
static void
XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
{
@ -1373,6 +1382,16 @@ XPC_WN_ModsAllowed_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id)
enumFlag, nullptr);
}
#define XPC_WN_SHARED_PROTO_CLASS_EXT \
{ \
nullptr, /* outerObject */ \
nullptr, /* innerObject */ \
nullptr, /* iteratorObject */ \
false, /* isWrappedNative */ \
nullptr, /* weakmapKeyDelegateOp */ \
XPC_WN_Shared_Proto_ObjectMoved \
}
const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
"XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
WRAPPER_SLOTS, // flags;
@ -1394,7 +1413,7 @@ const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
XPC_WN_Shared_Proto_Trace, // trace;
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
XPC_WN_SHARED_PROTO_CLASS_EXT,
XPC_WN_WithCall_ObjectOps
};
@ -1419,7 +1438,7 @@ const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
XPC_WN_Shared_Proto_Trace, // trace;
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
XPC_WN_SHARED_PROTO_CLASS_EXT,
XPC_WN_NoCall_ObjectOps
};
@ -1506,7 +1525,7 @@ const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
XPC_WN_Shared_Proto_Trace, // trace;
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
XPC_WN_SHARED_PROTO_CLASS_EXT,
XPC_WN_WithCall_ObjectOps
};
@ -1531,7 +1550,7 @@ const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
XPC_WN_Shared_Proto_Trace, // trace;
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
XPC_WN_SHARED_PROTO_CLASS_EXT,
XPC_WN_NoCall_ObjectOps
};
@ -1590,6 +1609,16 @@ XPC_WN_TearOff_Finalize(js::FreeOp *fop, JSObject *obj)
p->JSObjectFinalized();
}
static void
XPC_WN_TearOff_ObjectMoved(JSObject *obj, const JSObject *old)
{
XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
xpc_GetJSPrivate(obj);
if (!p)
return;
p->JSObjectMoved(obj, old);
}
const js::Class XPC_WN_Tearoff_JSClass = {
"WrappedNative_TearOff", // name;
WRAPPER_SLOTS, // flags;
@ -1601,5 +1630,22 @@ const js::Class XPC_WN_Tearoff_JSClass = {
XPC_WN_TearOff_Enumerate, // enumerate;
XPC_WN_TearOff_Resolve, // resolve;
XPC_WN_Shared_Convert, // convert;
XPC_WN_TearOff_Finalize // finalize;
XPC_WN_TearOff_Finalize, // finalize;
/* Optionally non-null members start here. */
nullptr, // call
nullptr, // construct
nullptr, // hasInstance
nullptr, // trace
JS_NULL_CLASS_SPEC,
// ClassExtension
{
nullptr, // outerObject
nullptr, // innerObject
nullptr, // iteratorObject
false, // isWrappedNative
nullptr, // weakmapKeyDelegateOp
XPC_WN_TearOff_ObjectMoved
},
};

Просмотреть файл

@ -142,6 +142,13 @@ XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj)
mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
}
void
XPCWrappedNativeProto::JSProtoObjectMoved(JSObject *obj, const JSObject *old)
{
MOZ_ASSERT(mJSProtoObject == old);
mJSProtoObject.init(obj); // Update without triggering barriers.
}
void
XPCWrappedNativeProto::SystemIsBeingShutDown()
{

Просмотреть файл

@ -487,10 +487,11 @@ XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt,
// static
void
XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt)
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(XPCJSRuntime* rt)
{
// We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
// calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
// If this is called from the finalization callback in JSGC_MARK_END then
// JSGC_FINALIZE_END must always follow it calling
// FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
// KillDyingScopes.
MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
@ -504,8 +505,11 @@ XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* r
XPCWrappedNativeScope* next = cur->mNext;
// Check for finalization of the global object. Note that global
// objects are never moved, so we don't need to handle updating the
// object pointer here.
if (cur->mGlobalJSObject && cur->mGlobalJSObject.isAboutToBeFinalized()) {
cur->mGlobalJSObject.finalize(fop->runtime());
cur->mGlobalJSObject.finalize(rt->Runtime());
// Move this scope from the live list to the dying list.
if (prev)
prev->mNext = next;
@ -515,19 +519,13 @@ XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* r
gDyingScopes = cur;
cur = nullptr;
}
if (cur)
prev = cur;
cur = next;
}
}
// static
void
XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC()
{
KillDyingScopes();
}
static PLDHashOperator
WrappedNativeMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
uint32_t number_t, void *arg)

Просмотреть файл

@ -585,6 +585,7 @@ public:
JSFinalizeStatus status,
bool isCompartmentGC,
void *data);
static void MovingGCCallback(JSRuntime *rt, void *data);
inline void AddVariantRoot(XPCTraceableVariant* variant);
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
@ -629,6 +630,8 @@ public:
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
void OnAfterProcessNextEvent() { mSlowScriptCheckpoint = mozilla::TimeStamp(); }
nsTArray<nsXPCWrappedJS*>& WrappedJSToReleaseArray() { return mWrappedJSToReleaseArray; }
private:
XPCJSRuntime(); // no implementation
explicit XPCJSRuntime(nsXPConnect* aXPConnect);
@ -1083,12 +1086,6 @@ public:
static void
SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb);
static void
StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt);
static void
FinishedFinalizationPhaseOfGC();
static void
MarkAllWrappedNativesAndProtos();
@ -1100,6 +1097,12 @@ public:
static void
SweepAllWrappedNativeTearOffs();
static void
UpdateWeakPointersAfterGC(XPCJSRuntime* rt);
static void
KillDyingScopes();
static void
DebugDumpAllScopes(int16_t depth);
@ -1179,8 +1182,6 @@ public:
protected:
virtual ~XPCWrappedNativeScope();
static void KillDyingScopes();
XPCWrappedNativeScope(); // not implemented
private:
@ -1850,6 +1851,7 @@ public:
bool CallPostCreatePrototype();
void JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj);
void JSProtoObjectMoved(JSObject *obj, const JSObject *old);
void SystemIsBeingShutDown();
@ -1944,6 +1946,7 @@ public:
void SetJSObject(JSObject* JSObj);
void JSObjectFinalized() {SetJSObject(nullptr);}
void JSObjectMoved(JSObject *obj, const JSObject *old);
XPCWrappedNativeTearOff()
: mInterface(nullptr), mNative(nullptr), mJSObject(nullptr) {}