Bug 650161 - Unify the finalization and moving GC callbacks into a weak pointer update callback r=terrence r=bholley

This commit is contained in:
Jon Coppeard 2014-09-24 12:54:11 +01:00
parent f93dcea4d5
commit 23f8fad496
19 changed files with 159 additions and 132 deletions

View File

@ -21,17 +21,9 @@ using namespace mozilla::jsipc;
using mozilla::AutoSafeJSContext; using mozilla::AutoSafeJSContext;
static void static void
FinalizeChild(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data) UpdateChildWeakPointersAfterGC(JSRuntime *rt, void *data)
{ {
if (status == JSFINALIZE_GROUP_START) { static_cast<JavaScriptChild *>(data)->updateWeakPointers();
static_cast<JavaScriptChild *>(data)->finalize();
}
}
static void
FixupChildAfterMovingGC(JSRuntime *rt, void *data)
{
static_cast<JavaScriptChild *>(data)->fixupAfterMovingGC();
} }
JavaScriptChild::JavaScriptChild(JSRuntime *rt) JavaScriptChild::JavaScriptChild(JSRuntime *rt)
@ -42,8 +34,7 @@ JavaScriptChild::JavaScriptChild(JSRuntime *rt)
JavaScriptChild::~JavaScriptChild() JavaScriptChild::~JavaScriptChild()
{ {
JS_RemoveFinalizeCallback(rt_, FinalizeChild); JS_RemoveWeakPointerCallback(rt_, UpdateChildWeakPointersAfterGC);
JS_RemoveMovingGCCallback(rt_, FixupChildAfterMovingGC);
} }
bool bool
@ -54,13 +45,12 @@ JavaScriptChild::init()
if (!WrapperAnswer::init()) if (!WrapperAnswer::init())
return false; return false;
JS_AddFinalizeCallback(rt_, FinalizeChild, this); JS_AddWeakPointerCallback(rt_, UpdateChildWeakPointersAfterGC, this);
JS_AddMovingGCCallback(rt_, FixupChildAfterMovingGC, this);
return true; return true;
} }
void void
JavaScriptChild::finalize() JavaScriptChild::updateWeakPointers()
{ {
objects_.sweep(); objects_.sweep();
objectIds_.sweep(); objectIds_.sweep();

View File

@ -21,7 +21,7 @@ class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
virtual ~JavaScriptChild(); virtual ~JavaScriptChild();
bool init(); bool init();
void finalize(); void updateWeakPointers();
void drop(JSObject *obj); void drop(JSObject *obj);

View File

@ -27,12 +27,6 @@ TraceParent(JSTracer *trc, void *data)
static_cast<JavaScriptParent *>(data)->trace(trc); static_cast<JavaScriptParent *>(data)->trace(trc);
} }
static void
FixupParentAfterMovingGC(JSRuntime *rt, void *data)
{
static_cast<JavaScriptParent *>(data)->fixupAfterMovingGC();
}
JavaScriptParent::JavaScriptParent(JSRuntime *rt) JavaScriptParent::JavaScriptParent(JSRuntime *rt)
: JavaScriptShared(rt), : JavaScriptShared(rt),
JavaScriptBase<PJavaScriptParent>(rt) JavaScriptBase<PJavaScriptParent>(rt)
@ -42,7 +36,6 @@ JavaScriptParent::JavaScriptParent(JSRuntime *rt)
JavaScriptParent::~JavaScriptParent() JavaScriptParent::~JavaScriptParent()
{ {
JS_RemoveExtraGCRootsTracer(rt_, TraceParent, this); JS_RemoveExtraGCRootsTracer(rt_, TraceParent, this);
JS_RemoveMovingGCCallback(rt_, FixupParentAfterMovingGC);
} }
bool bool
@ -52,15 +45,16 @@ JavaScriptParent::init()
return false; return false;
JS_AddExtraGCRootsTracer(rt_, TraceParent, this); JS_AddExtraGCRootsTracer(rt_, TraceParent, this);
JS_AddMovingGCCallback(rt_, FixupParentAfterMovingGC, this);
return true; return true;
} }
void void
JavaScriptParent::trace(JSTracer *trc) JavaScriptParent::trace(JSTracer *trc)
{ {
if (active()) if (active()) {
objects_.trace(trc); objects_.trace(trc);
objectIds_.trace(trc);
}
} }
JSObject * JSObject *

View File

@ -36,7 +36,6 @@ IdToObjectMap::trace(JSTracer *trc)
for (Table::Range r(table_.all()); !r.empty(); r.popFront()) { for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
DebugOnly<JSObject *> prior = r.front().value().get(); DebugOnly<JSObject *> prior = r.front().value().get();
JS_CallObjectTracer(trc, &r.front().value(), "ipc-object"); JS_CallObjectTracer(trc, &r.front().value(), "ipc-object");
MOZ_ASSERT(r.front().value() == prior);
} }
} }
@ -44,8 +43,9 @@ void
IdToObjectMap::sweep() IdToObjectMap::sweep()
{ {
for (Table::Enum e(table_); !e.empty(); e.popFront()) { for (Table::Enum e(table_); !e.empty(); e.popFront()) {
DebugOnly<JSObject *> prior = e.front().value().get(); JS::Heap<JSObject *> *objp = &e.front().value();
if (JS_IsAboutToBeFinalized(&e.front().value())) JS_UpdateWeakPointerAfterGC(objp);
if (!*objp)
e.removeFront(); e.removeFront();
} }
} }
@ -94,12 +94,24 @@ ObjectToIdMap::init()
return table_ && table_->init(32); return table_ && table_->init(32);
} }
void
ObjectToIdMap::trace(JSTracer *trc)
{
for (Table::Enum e(*table_); !e.empty(); e.popFront()) {
JSObject *obj = e.front().key();
JS_CallUnbarrieredObjectTracer(trc, &obj, "ipc-object");
if (obj != e.front().key())
e.rekeyFront(obj);
}
}
void void
ObjectToIdMap::sweep() ObjectToIdMap::sweep()
{ {
for (Table::Enum e(*table_); !e.empty(); e.popFront()) { for (Table::Enum e(*table_); !e.empty(); e.popFront()) {
JSObject *obj = e.front().key(); JSObject *obj = e.front().key();
if (JS_IsAboutToBeFinalizedUnbarriered(&obj)) JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
if (!obj)
e.removeFront(); e.removeFront();
else if (obj != e.front().key()) else if (obj != e.front().key())
e.rekeyFront(obj); e.rekeyFront(obj);
@ -568,9 +580,3 @@ JavaScriptShared::Wrap(JSContext *cx, HandleObject aObj, InfallibleTArray<CpowEn
return true; return true;
} }
void JavaScriptShared::fixupAfterMovingGC()
{
objects_.sweep();
cpows_.sweep();
objectIds_.sweep();
}

View File

@ -54,8 +54,6 @@ class IdToObjectMap
JSObject *find(ObjectId id); JSObject *find(ObjectId id);
void remove(ObjectId id); void remove(ObjectId id);
void fixupAfterMovingGC();
private: private:
Table table_; Table table_;
}; };
@ -71,14 +69,13 @@ class ObjectToIdMap
~ObjectToIdMap(); ~ObjectToIdMap();
bool init(); bool init();
void trace(JSTracer *trc);
void sweep(); void sweep();
bool add(JSContext *cx, JSObject *obj, ObjectId id); bool add(JSContext *cx, JSObject *obj, ObjectId id);
ObjectId find(JSObject *obj); ObjectId find(JSObject *obj);
void remove(JSObject *obj); void remove(JSObject *obj);
void fixupAfterMovingGC();
private: private:
static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data); static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data);
@ -104,8 +101,6 @@ class JavaScriptShared
bool Unwrap(JSContext *cx, const InfallibleTArray<CpowEntry> &aCpows, JS::MutableHandleObject objp); bool Unwrap(JSContext *cx, const InfallibleTArray<CpowEntry> &aCpows, JS::MutableHandleObject objp);
bool Wrap(JSContext *cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry> *outCpows); bool Wrap(JSContext *cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry> *outCpows);
void fixupAfterMovingGC();
protected: protected:
bool toVariant(JSContext *cx, JS::HandleValue from, JSVariant *to); bool toVariant(JSContext *cx, JS::HandleValue from, JSVariant *to);
bool fromVariant(JSContext *cx, const JSVariant &from, JS::MutableHandleValue to); bool fromVariant(JSContext *cx, const JSVariant &from, JS::MutableHandleValue to);

View File

@ -31,7 +31,7 @@ OwnerOf(JSObject *obj)
} }
ObjectId ObjectId
WrapperOwner::idOf(JSObject *obj) WrapperOwner::idOfUnchecked(JSObject *obj)
{ {
MOZ_ASSERT(IsCPOW(obj)); MOZ_ASSERT(IsCPOW(obj));
@ -39,12 +39,19 @@ WrapperOwner::idOf(JSObject *obj)
MOZ_ASSERT(v.isDouble()); MOZ_ASSERT(v.isDouble());
ObjectId objId = BitwiseCast<uint64_t>(v.toDouble()); ObjectId objId = BitwiseCast<uint64_t>(v.toDouble());
MOZ_ASSERT(findCPOWById(objId) == obj);
MOZ_ASSERT(objId); MOZ_ASSERT(objId);
return objId; return objId;
} }
ObjectId
WrapperOwner::idOf(JSObject *obj)
{
ObjectId objId = idOfUnchecked(obj);
MOZ_ASSERT(findCPOWById(objId) == obj);
return objId;
}
class CPOWProxyHandler : public BaseProxyHandler class CPOWProxyHandler : public BaseProxyHandler
{ {
public: public:
@ -84,6 +91,7 @@ class CPOWProxyHandler : public BaseProxyHandler
JSContext *cx) const MOZ_OVERRIDE; JSContext *cx) const MOZ_OVERRIDE;
virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE; virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
virtual void objectMoved(JSObject *proxy, const JSObject *old) const MOZ_OVERRIDE;
virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE; virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE; virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
@ -648,6 +656,12 @@ CPOWProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const
OwnerOf(proxy)->drop(proxy); OwnerOf(proxy)->drop(proxy);
} }
void
CPOWProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const
{
OwnerOf(proxy)->updatePointer(proxy, old);
}
bool bool
CPOWProxyHandler::isCallable(JSObject *obj) const CPOWProxyHandler::isCallable(JSObject *obj) const
{ {
@ -678,6 +692,14 @@ WrapperOwner::drop(JSObject *obj)
decref(); decref();
} }
void
WrapperOwner::updatePointer(JSObject *obj, const JSObject *old)
{
ObjectId objId = idOfUnchecked(obj);
MOZ_ASSERT(findCPOWById(objId) == old);
cpows_.add(objId, obj);
}
bool bool
WrapperOwner::init() WrapperOwner::init()
{ {

View File

@ -76,6 +76,7 @@ class WrapperOwner : public virtual JavaScriptShared
bool active() { return !inactive_; } bool active() { return !inactive_; }
void drop(JSObject *obj); void drop(JSObject *obj);
void updatePointer(JSObject *obj, const JSObject *old);
virtual void ActorDestroy(ActorDestroyReason why); virtual void ActorDestroy(ActorDestroyReason why);
@ -88,6 +89,8 @@ class WrapperOwner : public virtual JavaScriptShared
ObjectId idOf(JSObject *obj); ObjectId idOf(JSObject *obj);
private: private:
ObjectId idOfUnchecked(JSObject *obj);
bool getPropertyNames(JSContext *cx, JS::HandleObject proxy, uint32_t flags, bool getPropertyNames(JSContext *cx, JS::HandleObject proxy, uint32_t flags,
JS::AutoIdVector &props); JS::AutoIdVector &props);

View File

@ -1207,7 +1207,7 @@ class JS_PUBLIC_API(ObjectPtr)
IncrementalObjectBarrier(value); IncrementalObjectBarrier(value);
} }
bool isAboutToBeFinalized(); void updateWeakPointerAfterGC();
ObjectPtr &operator=(JSObject *obj) { ObjectPtr &operator=(JSObject *obj) {
IncrementalObjectBarrier(value); IncrementalObjectBarrier(value);

View File

@ -414,8 +414,8 @@ class GCRuntime
void setGCCallback(JSGCCallback callback, void *data); void setGCCallback(JSGCCallback callback, void *data);
bool addFinalizeCallback(JSFinalizeCallback callback, void *data); bool addFinalizeCallback(JSFinalizeCallback callback, void *data);
void removeFinalizeCallback(JSFinalizeCallback func); void removeFinalizeCallback(JSFinalizeCallback func);
bool addMovingGCCallback(JSMovingGCCallback callback, void *data); bool addWeakPointerCallback(JSWeakPointerCallback callback, void *data);
void removeMovingGCCallback(JSMovingGCCallback func); void removeWeakPointerCallback(JSWeakPointerCallback func);
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback); JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
void setValidate(bool enable); void setValidate(bool enable);
@ -550,7 +550,7 @@ class GCRuntime
#endif #endif
void callFinalizeCallbacks(FreeOp *fop, JSFinalizeStatus status) const; void callFinalizeCallbacks(FreeOp *fop, JSFinalizeStatus status) const;
void callMovingGCCallbacks() const; void callWeakPointerCallbacks() const;
public: public:
JSRuntime *rt; JSRuntime *rt;
@ -805,7 +805,7 @@ class GCRuntime
Callback<JSGCCallback> gcCallback; Callback<JSGCCallback> gcCallback;
CallbackVector<JSFinalizeCallback> finalizeCallbacks; CallbackVector<JSFinalizeCallback> finalizeCallbacks;
CallbackVector<JSMovingGCCallback> movingCallbacks; CallbackVector<JSWeakPointerCallback> updateWeakPointerCallbacks;
/* /*
* Malloc counter to measure memory pressure for GC scheduling. It runs * Malloc counter to measure memory pressure for GC scheduling. It runs

View File

@ -1888,28 +1888,29 @@ JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
JS_AddMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb, void *data) JS_AddWeakPointerCallback(JSRuntime *rt, JSWeakPointerCallback cb, void *data)
{ {
AssertHeapIsIdle(rt); AssertHeapIsIdle(rt);
return rt->gc.addMovingGCCallback(cb, data); return rt->gc.addWeakPointerCallback(cb, data);
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_RemoveMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb) JS_RemoveWeakPointerCallback(JSRuntime *rt, JSWeakPointerCallback cb)
{ {
rt->gc.removeMovingGCCallback(cb); rt->gc.removeWeakPointerCallback(cb);
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(void)
JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp) JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject *> *objp)
{ {
return IsObjectAboutToBeFinalized(objp->unsafeGet()); JS_UpdateWeakPointerAfterGCUnbarriered(objp->unsafeGet());
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(void)
JS_IsAboutToBeFinalizedUnbarriered(JSObject **objp) JS_UpdateWeakPointerAfterGCUnbarriered(JSObject **objp)
{ {
return IsObjectAboutToBeFinalized(objp); if (IsObjectAboutToBeFinalized(objp))
*objp = nullptr;
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(void)

View File

@ -690,7 +690,7 @@ typedef void
(* JSFinalizeCallback)(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data); (* JSFinalizeCallback)(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data);
typedef void typedef void
(* JSMovingGCCallback)(JSRuntime *rt, void *data); (* JSWeakPointerCallback)(JSRuntime *rt, void *data);
typedef bool typedef bool
(* JSInterruptCallback)(JSContext *cx); (* JSInterruptCallback)(JSContext *cx);
@ -2038,12 +2038,6 @@ JS_AddFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb, void *data);
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb); JS_RemoveFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb);
extern JS_PUBLIC_API(bool)
JS_AddMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb, void *data);
extern JS_PUBLIC_API(void)
JS_RemoveMovingGCCallback(JSRuntime *rt, JSMovingGCCallback cb);
extern JS_PUBLIC_API(bool) extern JS_PUBLIC_API(bool)
JS_IsGCMarkingTracer(JSTracer *trc); JS_IsGCMarkingTracer(JSTracer *trc);
@ -2054,26 +2048,41 @@ JS_IsMarkingGray(JSTracer *trc);
#endif #endif
/* /*
* JS_IsAboutToBeFinalized checks if the given object is going to be finalized * Weak pointers and garbage collection
* at the end of the current GC. When called outside of the context of a GC, *
* this function will return false. Typically this function is used on weak * Weak pointers are by their nature not marked as part of garbage collection,
* references, where the reference should be nulled out or destroyed if the * but they may need to be updated in two cases after a GC:
* given object is about to be finalized. *
* 1) Their referent was found not to be live and is about to be finalized
* 2) Their referent has been moved by a compacting GC
*
* To handle this, any part of the system that maintain weak pointers to
* JavaScript GC things must register a callback with
* JS_(Add,Remove)WeakPointerCallback(). This callback must then call
* JS_UpdateWeakPointerAfterGC() on all weak pointers it knows about.
*
* The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
* referent is about to be finalized the pointer will be set to null. If the
* referent has been moved then the pointer will be updated to point to the new
* location.
* *
* The argument to JS_IsAboutToBeFinalized is an in-out param: when the
* function returns false, the object being referenced is still alive, but the
* garbage collector might have moved it. In this case, the reference passed
* to JS_IsAboutToBeFinalized will be updated to the object's new location.
* Callers of this method are responsible for updating any state that is * Callers of this method are responsible for updating any state that is
* dependent on the object's address. For example, if the object's address is * dependent on the object's address. For example, if the object's address is
* used as a key in a hashtable, then the object must be removed and * used as a key in a hashtable, then the object must be removed and
* re-inserted with the correct hash. * re-inserted with the correct hash.
*/ */
extern JS_PUBLIC_API(bool)
JS_IsAboutToBeFinalized(JS::Heap<JSObject *> *objp);
extern JS_PUBLIC_API(bool) extern JS_PUBLIC_API(bool)
JS_IsAboutToBeFinalizedUnbarriered(JSObject **objp); JS_AddWeakPointerCallback(JSRuntime *rt, JSWeakPointerCallback cb, void *data);
extern JS_PUBLIC_API(void)
JS_RemoveWeakPointerCallback(JSRuntime *rt, JSWeakPointerCallback cb);
extern JS_PUBLIC_API(void)
JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject *> *objp);
extern JS_PUBLIC_API(void)
JS_UpdateWeakPointerAfterGCUnbarriered(JSObject **objp);
typedef enum JSGCParamKey { typedef enum JSGCParamKey {
/* Maximum nominal heap before last ditch GC. */ /* Maximum nominal heap before last ditch GC. */

View File

@ -1235,10 +1235,11 @@ js::GetAnyCompartmentInZone(JS::Zone *zone)
return comp.get(); return comp.get();
} }
bool void
JS::ObjectPtr::isAboutToBeFinalized() JS::ObjectPtr::updateWeakPointerAfterGC()
{ {
return JS_IsAboutToBeFinalized(&value); if (js::gc::IsObjectAboutToBeFinalized(value.unsafeGet()))
value = nullptr;
} }
void void

View File

@ -1623,29 +1623,29 @@ GCRuntime::callFinalizeCallbacks(FreeOp *fop, JSFinalizeStatus status) const
} }
bool bool
GCRuntime::addMovingGCCallback(JSMovingGCCallback callback, void *data) GCRuntime::addWeakPointerCallback(JSWeakPointerCallback callback, void *data)
{ {
return movingCallbacks.append(Callback<JSMovingGCCallback>(callback, data)); return updateWeakPointerCallbacks.append(Callback<JSWeakPointerCallback>(callback, data));
} }
void void
GCRuntime::removeMovingGCCallback(JSMovingGCCallback callback) GCRuntime::removeWeakPointerCallback(JSWeakPointerCallback callback)
{ {
for (Callback<JSMovingGCCallback> *p = movingCallbacks.begin(); for (Callback<JSWeakPointerCallback> *p = updateWeakPointerCallbacks.begin();
p < movingCallbacks.end(); p++) p < updateWeakPointerCallbacks.end(); p++)
{ {
if (p->op == callback) { if (p->op == callback) {
movingCallbacks.erase(p); updateWeakPointerCallbacks.erase(p);
break; break;
} }
} }
} }
void void
GCRuntime::callMovingGCCallbacks() const GCRuntime::callWeakPointerCallbacks() const
{ {
for (const Callback<JSMovingGCCallback> *p = movingCallbacks.begin(); for (const Callback<JSWeakPointerCallback> *p = updateWeakPointerCallbacks.begin();
p < movingCallbacks.end(); p++) p < updateWeakPointerCallbacks.end(); p++)
{ {
p->op(rt, p->data); p->op(rt, p->data);
} }
@ -2461,7 +2461,7 @@ GCRuntime::updatePointersToRelocatedCells()
MovingTracer::Sweep(&trc); MovingTracer::Sweep(&trc);
// Call callbacks to get the rest of the system to fixup other untraced pointers. // Call callbacks to get the rest of the system to fixup other untraced pointers.
callMovingGCCallbacks(); callWeakPointerCallbacks();
} }
void void
@ -4636,6 +4636,7 @@ GCRuntime::beginSweepingZoneGroup()
{ {
gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START); gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START); callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
callWeakPointerCallbacks();
} }
if (sweepingAtoms) { if (sweepingAtoms) {

View File

@ -789,17 +789,6 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop,
MOZ_ASSERT(!self->mGCIsRunning, "bad state"); MOZ_ASSERT(!self->mGCIsRunning, "bad state");
self->mGCIsRunning = true; self->mGCIsRunning = true;
// 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->UpdateWeakPointersAfterGC(self);
// Find dying scopes.
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
self->mDoingFinalization = true; self->mDoingFinalization = true;
break; break;
} }
@ -969,14 +958,14 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop,
} }
/* static */ void /* static */ void
XPCJSRuntime::MovingGCCallback(JSRuntime *rt, void *data) XPCJSRuntime::WeakPointerCallback(JSRuntime *rt, void *data)
{ {
// Called to fixup any weak GC thing pointers that may have been moved. // Called to remove any weak pointers to GC things that are about to be
// finalized and fixup any pointers that may have been moved.
XPCJSRuntime *self = static_cast<XPCJSRuntime *>(data); XPCJSRuntime *self = static_cast<XPCJSRuntime *>(data);
self->mWrappedJSMap->UpdateWeakPointersAfterGC(self); self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
MOZ_ASSERT(self->WrappedJSToReleaseArray().IsEmpty());
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self); XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
} }
@ -1547,7 +1536,7 @@ XPCJSRuntime::~XPCJSRuntime()
// callback if we aren't careful. Null out the relevant callbacks. // callback if we aren't careful. Null out the relevant callbacks.
js::SetActivityCallback(Runtime(), nullptr, nullptr); js::SetActivityCallback(Runtime(), nullptr, nullptr);
JS_RemoveFinalizeCallback(Runtime(), FinalizeCallback); JS_RemoveFinalizeCallback(Runtime(), FinalizeCallback);
JS_RemoveMovingGCCallback(Runtime(), MovingGCCallback); JS_RemoveWeakPointerCallback(Runtime(), WeakPointerCallback);
// Clear any pending exception. It might be an XPCWrappedJS, and if we try // Clear any pending exception. It might be an XPCWrappedJS, and if we try
// to destroy it later we will crash. // to destroy it later we will crash.
@ -3246,7 +3235,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback); JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback); mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
JS_AddFinalizeCallback(runtime, FinalizeCallback, nullptr); JS_AddFinalizeCallback(runtime, FinalizeCallback, nullptr);
JS_AddMovingGCCallback(runtime, MovingGCCallback, this); JS_AddWeakPointerCallback(runtime, WeakPointerCallback, this);
JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks); JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
js::SetPreserveWrapperCallback(runtime, PreserveWrapper); js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER

View File

@ -88,8 +88,11 @@ void
JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime *runtime) JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime *runtime)
{ {
// Check all wrappers and update their JSObject pointer if it has been // 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 // moved, or if it is about to be finalized queue the wrapper for
// finalized. // destruction by adding it to an array held by the runtime.
// 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.
nsTArray<nsXPCWrappedJS*> &dying = runtime->WrappedJSToReleaseArray(); nsTArray<nsXPCWrappedJS*> &dying = runtime->WrappedJSToReleaseArray();
MOZ_ASSERT(dying.IsEmpty()); MOZ_ASSERT(dying.IsEmpty());
@ -102,24 +105,29 @@ JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime *runtime)
while (wrapper) { while (wrapper) {
#ifdef DEBUG #ifdef DEBUG
if (!wrapper->IsSubjectToFinalization()) { if (!wrapper->IsSubjectToFinalization()) {
// If a wrapper is not subject to finalization then it roots its
// JS object. If so, then it will not be about to be finalized
// and any necessary pointer update will have already happened
// when it was marked.
JSObject *obj = wrapper->GetJSObjectPreserveColor(); JSObject *obj = wrapper->GetJSObjectPreserveColor();
JSObject *prior = obj; JSObject *prior = obj;
MOZ_ASSERT(!JS_IsAboutToBeFinalizedUnbarriered(&obj)); JS_UpdateWeakPointerAfterGCUnbarriered(&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); MOZ_ASSERT(obj == prior);
} }
#endif #endif
if (wrapper->IsSubjectToFinalization() && wrapper->IsObjectAboutToBeFinalized()) if (wrapper->IsSubjectToFinalization()) {
dying.AppendElement(wrapper); wrapper->UpdateObjectPointerAfterGC();
if (!wrapper->GetJSObjectPreserveColor())
dying.AppendElement(wrapper);
}
wrapper = wrapper->GetNextWrapper(); wrapper = wrapper->GetNextWrapper();
} }
// Remove or update the JSObject key in the table if necessary. // Remove or update the JSObject key in the table if necessary.
JSObject *obj = e.front().key(); JSObject *obj = e.front().key();
JSObject *prior = obj; JSObject *prior = obj;
if (JS_IsAboutToBeFinalizedUnbarriered(&obj)) JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
if (!obj)
e.removeFront(); e.removeFront();
else if (obj != prior) else if (obj != prior)
e.rekeyFront(obj); e.rekeyFront(obj);

View File

@ -657,11 +657,14 @@ public:
void Sweep() { void Sweep() {
for (Map::Enum e(mTable); !e.empty(); e.popFront()) { for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
JSObject *updated = e.front().key(); JSObject *key = e.front().key();
if (JS_IsAboutToBeFinalizedUnbarriered(&updated) || JS_IsAboutToBeFinalized(&e.front().value())) JS::Heap<JSObject *> *valuep = &e.front().value();
JS_UpdateWeakPointerAfterGCUnbarriered(&key);
JS_UpdateWeakPointerAfterGC(valuep);
if (!key || !*valuep)
e.removeFront(); e.removeFront();
else if (updated != e.front().key()) else if (key != e.front().key())
e.rekeyFront(updated); e.rekeyFront(key);
} }
} }

View File

@ -940,8 +940,11 @@ XPCWrappedNative::FlatJSObjectFinalized()
for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) { for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
JSObject* jso = to->GetJSObjectPreserveColor(); JSObject* jso = to->GetJSObjectPreserveColor();
if (jso) { if (jso) {
MOZ_ASSERT(JS_IsAboutToBeFinalizedUnbarriered(&jso));
JS_SetPrivate(jso, nullptr); JS_SetPrivate(jso, nullptr);
#ifdef DEBUG
JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
MOZ_ASSERT(!jso);
#endif
to->JSObjectFinalized(); to->JSObjectFinalized();
} }

View File

@ -508,16 +508,18 @@ XPCWrappedNativeScope::UpdateWeakPointersAfterGC(XPCJSRuntime* rt)
// Check for finalization of the global object. Note that global // Check for finalization of the global object. Note that global
// objects are never moved, so we don't need to handle updating the // objects are never moved, so we don't need to handle updating the
// object pointer here. // object pointer here.
if (cur->mGlobalJSObject && cur->mGlobalJSObject.isAboutToBeFinalized()) { if (cur->mGlobalJSObject) {
cur->mGlobalJSObject.finalize(rt->Runtime()); cur->mGlobalJSObject.updateWeakPointerAfterGC();
// Move this scope from the live list to the dying list. if (!cur->mGlobalJSObject) {
if (prev) // Move this scope from the live list to the dying list.
prev->mNext = next; if (prev)
else prev->mNext = next;
gScopes = next; else
cur->mNext = gDyingScopes; gScopes = next;
gDyingScopes = cur; cur->mNext = gDyingScopes;
cur = nullptr; gDyingScopes = cur;
cur = nullptr;
}
} }
if (cur) if (cur)

View File

@ -585,7 +585,7 @@ public:
JSFinalizeStatus status, JSFinalizeStatus status,
bool isCompartmentGC, bool isCompartmentGC,
void *data); void *data);
static void MovingGCCallback(JSRuntime *rt, void *data); static void WeakPointerCallback(JSRuntime *rt, void *data);
inline void AddVariantRoot(XPCTraceableVariant* variant); inline void AddVariantRoot(XPCTraceableVariant* variant);
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS); inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
@ -2471,7 +2471,7 @@ public:
// to find non-rooting wrappers for dying JS objects. See the top of // to find non-rooting wrappers for dying JS objects. See the top of
// XPCWrappedJS.cpp for more details. // XPCWrappedJS.cpp for more details.
bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;} bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
bool IsObjectAboutToBeFinalized() {return JS_IsAboutToBeFinalized(&mJSObj);} void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);}
bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;} bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;}
nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;} nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}