Bug 1288909, part 1 - Implement refcounting of XPCNativeSet. r=billm

This patch is similar to bug 1288870.

Strong references:

- XPCCallContext::mSet: Like XPCNativeInterface, this only roots it
when |mState >= HAVE_NAME|, and again this only requires changing
SystemIsBeingShutDown().

- XPCWrappedNativeProto::mSet and XPCWrappedNative::mSet. These become
RefPtrs.

- stack: AutoMarkingNativeSetPtr become RefPtr<XPCNativeSet>. This
lets me eliminate some uses of AutoJSContext. This is the bulk of the
patch.

Weak references:

- mNativeSetMap. This reference gets cleared in the dtor. This
requires bug 1290239 to actually find the entry for removal.

- mClassInfo2NativeSetMap. The reference is in the value for this hash
table, and we don't have the key in the set dtor. Fortunately, the
only code that adds to this table is
XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo), which in turn is
only called by GetNewOrUsed(nsIClassInfo* classInfo). This code
creates a new XPCWrappedNativeProto, which (with my patch) holds a
strong reference to the set that has been added to the table. This set
is never changed or released until the dtor for the proto, which calls
ClearCacheEntryForClassInfo(), removing the entry from the
hashtable. Thus, the lifetime of the set is always going to be longer
than the lifetime of the entry.

Other notes:

- Like XPCNativeInterface, this class uses placement |new| that
requires a special destruction function, which with my patch is hidden
away in the refcounting code.

- This patch delete a bunch of marking/sweeping code from
XPCJSRuntime::FinalizeCallback(), because the lifetimes are managed by
the refcounting now. Some of the marking code is left behind to be
cleaned up in a later patch.

- I didn't see any methods that had XPCNativeSet** outparams.

- MOZ_COUNT_{CTOR,DTOR}(XPCNativeSet) is not needed because it is now
refcounted.

MozReview-Commit-ID: 7oTorCwda1n

--HG--
extra : rebase_source : 0c477e18c405e4ea88393279cf8bea62c5b0f4c7
This commit is contained in:
Andrew McCreight 2016-07-27 16:38:30 -07:00
Родитель bd57d227da
Коммит bbaa5c3f54
8 изменённых файлов: 79 добавлений и 120 удалений

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

@ -199,6 +199,7 @@ XPCCallContext::SystemIsBeingShutDown()
NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
mXPCJSContext = nullptr;
mState = SYSTEM_SHUTDOWN;
mSet = nullptr;
mInterface = nullptr;
if (mPrevCallContext)

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

@ -820,37 +820,6 @@ XPCJSContext::FinalizeCallback(JSFreeOp* fop,
}
}
// Do the sweeping. During a zone GC, only WrappedNativeProtos in
// collected zones will be marked. Therefore, some reachable
// NativeInterfaces will not be marked, so it is not safe to sweep
// them. We still need to unmark them, since the ones pointed to by
// WrappedNativeProtos in a zone being collected will be marked.
//
// Ideally, if NativeInterfaces from different zones were kept
// separate, we could sweep only the ones belonging to zones being
// collected. Currently, though, NativeInterfaces are shared between
// zones. This ought to be fixed.
bool doSweep = !isZoneGC;
if (doSweep) {
for (auto i = self->mClassInfo2NativeSetMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<ClassInfo2NativeSetMap::Entry*>(i.Get());
if (!entry->value->IsMarked())
i.Remove();
}
}
for (auto i = self->mNativeSetMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
XPCNativeSet* set = entry->key_value;
if (set->IsMarked()) {
set->Unmark();
} else if (doSweep) {
XPCNativeSet::DestroyInstance(set);
i.Remove();
}
}
#ifdef DEBUG
XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked();
#endif

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

@ -310,8 +310,6 @@ public:
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
// ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets.
// So we don't want to count those XPCNativeSets, because they are better
// counted elsewhere (i.e. in XPCJSContext::mNativeSetMap, which holds

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

@ -431,15 +431,15 @@ XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
if (!iface)
iface = XPCNativeInterface::GetISupports();
AutoMarkingNativeSetPtr set(cx);
XPCNativeSetKey key(iface);
set = XPCNativeSet::GetNewOrUsed(&key);
RefPtr<XPCNativeSet> set =
XPCNativeSet::GetNewOrUsed(&key);
if (!set)
return NS_ERROR_FAILURE;
wrapper =
new XPCWrappedNative(helper.forgetCanonical(), Scope, set);
wrapper = new XPCWrappedNative(helper.forgetCanonical(), Scope,
set.forget());
}
MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
@ -566,7 +566,7 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
// This ctor is used if this object will NOT have a proto.
XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
XPCWrappedNativeScope* aScope,
XPCNativeSet* aSet)
already_AddRefed<XPCNativeSet>&& aSet)
: mMaybeScope(TagScope(aScope)),
mSet(aSet),
@ -578,7 +578,7 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
MOZ_ASSERT(aScope, "bad ctor param");
MOZ_ASSERT(aSet, "bad ctor param");
MOZ_ASSERT(mSet, "bad ctor param");
}
XPCWrappedNative::~XPCWrappedNative()
@ -1014,16 +1014,14 @@ private:
bool
XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
{
AutoJSContext cx;
if (!mSet->HasInterface(aInterface)) {
AutoMarkingNativeSetPtr newSet(cx);
XPCNativeSetKey key(mSet, aInterface);
newSet = XPCNativeSet::GetNewOrUsed(&key);
RefPtr<XPCNativeSet> newSet =
XPCNativeSet::GetNewOrUsed(&key);
if (!newSet)
return false;
mSet = newSet;
mSet = newSet.forget();
}
return true;
}
@ -2164,7 +2162,7 @@ NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
if (depth && mSet)
mSet->DebugDump(depth);
else
XPC_LOG_ALWAYS(("mSet @ %x", mSet));
XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.getPtr()));
XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity.get()));

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

@ -462,13 +462,21 @@ XPCNativeSetKey::Hash() const
/***************************************************************************/
// XPCNativeSet
XPCNativeSet::~XPCNativeSet()
{
// Remove |this| before we clear the interfaces to ensure that the
// hashtable look up is correct.
XPCJSContext::Get()->GetNativeSetMap()->Remove(this);
for (int i = 0; i < mInterfaceCount; i++) {
NS_RELEASE(mInterfaces[i]);
}
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(const nsIID* iid)
{
AutoJSContext cx;
AutoMarkingNativeSetPtr set(cx);
RefPtr<XPCNativeInterface> iface =
XPCNativeInterface::GetNewOrUsed(iid);
if (!iface)
@ -481,10 +489,10 @@ XPCNativeSet::GetNewOrUsed(const nsIID* iid)
if (!map)
return nullptr;
set = map->Find(&key);
RefPtr<XPCNativeSet> set = map->Find(&key);
if (set)
return set;
return set.forget();
set = NewInstance({iface.forget()});
if (!set)
@ -492,29 +500,25 @@ XPCNativeSet::GetNewOrUsed(const nsIID* iid)
if (!map->AddNew(&key, set)) {
NS_ERROR("failed to add our set!");
DestroyInstance(set);
set = nullptr;
}
return set;
return set.forget();
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
{
AutoJSContext cx;
AutoMarkingNativeSetPtr set(cx);
XPCJSContext* xpccx = XPCJSContext::Get();
ClassInfo2NativeSetMap* map = xpccx->GetClassInfo2NativeSetMap();
if (!map)
return nullptr;
set = map->Find(classInfo);
RefPtr<XPCNativeSet> set = map->Find(classInfo);
if (set)
return set;
return set.forget();
nsIID** iidArray = nullptr;
uint32_t iidCount = 0;
@ -567,14 +571,12 @@ XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
XPCNativeSet* set2 = map2->Add(&key, set);
if (!set2) {
NS_ERROR("failed to add our set!");
DestroyInstance(set);
set = nullptr;
goto out;
}
// It is okay to find an existing entry here because
// we did not look for one before we called Add().
if (set2 != set) {
DestroyInstance(set);
set = set2;
}
}
@ -596,7 +598,7 @@ out:
if (iidArray)
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray);
return set;
return set.forget();
}
// static
@ -610,20 +612,17 @@ XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(XPCNativeSetKey* key)
{
AutoJSContext cx;
AutoMarkingNativeSetPtr set(cx);
XPCJSContext* xpccx = XPCJSContext::Get();
NativeSetMap* map = xpccx->GetNativeSetMap();
NativeSetMap* map = XPCJSContext::Get()->GetNativeSetMap();
if (!map)
return nullptr;
set = map->Find(key);
RefPtr<XPCNativeSet> set = map->Find(key);
if (set)
return set;
return set.forget();
if (key->GetBaseSet())
set = NewInstanceMutate(key);
@ -635,15 +634,14 @@ XPCNativeSet::GetNewOrUsed(XPCNativeSetKey* key)
if (!map->AddNew(key, set)) {
NS_ERROR("failed to add our set!");
DestroyInstance(set);
set = nullptr;
}
return set;
return set.forget();
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
XPCNativeSet* secondSet,
bool preserveFirstSetOrder)
@ -658,12 +656,12 @@ XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
// If everything in secondSet was a duplicate, we can just use the first
// set.
if (uniqueCount == firstSet->mInterfaceCount)
return firstSet;
return RefPtr<XPCNativeSet>(firstSet).forget();
// If the secondSet is just a superset of the first, we can use it provided
// that the caller doesn't care about ordering.
if (!preserveFirstSetOrder && uniqueCount == secondSet->mInterfaceCount)
return secondSet;
return RefPtr<XPCNativeSet>(secondSet).forget();
// Ok, darn. Now we have to make a new set.
//
@ -672,7 +670,7 @@ XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
// a lot of stuff assumes that sets are created by adding one interface to an
// existing set. So let's just do the slow and easy thing and hope that the
// above optimizations handle the common cases.
XPCNativeSet* currentSet = firstSet;
RefPtr<XPCNativeSet> currentSet = firstSet;
for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
XPCNativeInterface* iface = secondSet->mInterfaces[i];
if (!currentSet->HasInterface(iface)) {
@ -686,11 +684,11 @@ XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
// We've got the union set. Hand it back to the caller.
MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount);
return currentSet;
return currentSet.forget();
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array)
{
if (array.Length() == 0)
@ -715,7 +713,7 @@ XPCNativeSet::NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array)
if (slots > 1)
size += (slots - 1) * sizeof(XPCNativeInterface*);
void* place = new char[size];
XPCNativeSet* obj = new(place) XPCNativeSet();
RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
// Stick the nsISupports in front and skip additional nsISupport(s)
XPCNativeInterface** outp = (XPCNativeInterface**) &obj->mInterfaces;
@ -733,11 +731,11 @@ XPCNativeSet::NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array)
obj->mMemberCount = memberCount;
obj->mInterfaceCount = slots;
return obj;
return obj.forget();
}
// static
XPCNativeSet*
already_AddRefed<XPCNativeSet>
XPCNativeSet::NewInstanceMutate(XPCNativeSetKey* key)
{
XPCNativeSet* otherSet = key->GetBaseSet();
@ -753,7 +751,7 @@ XPCNativeSet::NewInstanceMutate(XPCNativeSetKey* key)
int size = sizeof(XPCNativeSet);
size += otherSet->mInterfaceCount * sizeof(XPCNativeInterface*);
void* place = new char[size];
XPCNativeSet* obj = new(place) XPCNativeSet();
RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
obj->mMemberCount = otherSet->GetMemberCount() +
newInterface->GetMemberCount();
@ -766,7 +764,7 @@ XPCNativeSet::NewInstanceMutate(XPCNativeSetKey* key)
}
NS_ADDREF(*dest++ = newInterface);
return obj;
return obj.forget();
}
// static

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

@ -17,7 +17,7 @@ int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0;
XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
nsIClassInfo* ClassInfo,
XPCNativeSet* Set)
already_AddRefed<XPCNativeSet>&& Set)
: mScope(Scope),
mJSProtoObject(nullptr),
mClassInfo(ClassInfo),
@ -166,12 +166,11 @@ XPCWrappedNativeProto::GetNewOrUsed(XPCWrappedNativeScope* scope,
if (proto)
return proto;
AutoMarkingNativeSetPtr set(cx);
set = XPCNativeSet::GetNewOrUsed(classInfo);
RefPtr<XPCNativeSet> set = XPCNativeSet::GetNewOrUsed(classInfo);
if (!set)
return nullptr;
proto = new XPCWrappedNativeProto(scope, classInfo, set);
proto = new XPCWrappedNativeProto(scope, classInfo, set.forget());
if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) {
delete proto.get();
@ -193,7 +192,7 @@ XPCWrappedNativeProto::DebugDump(int16_t depth)
XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount));
XPC_LOG_ALWAYS(("mScope @ %x", mScope));
XPC_LOG_ALWAYS(("mJSProtoObject @ %x", mJSProtoObject.get()));
XPC_LOG_ALWAYS(("mSet @ %x", mSet));
XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
if (depth && mScriptableInfo) {
XPC_LOG_INDENT();

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

@ -793,7 +793,7 @@ private:
XPCNativeScriptableInfo* mScriptableInfo;
XPCNativeSet* mSet;
RefPtr<XPCNativeSet> mSet;
RefPtr<XPCNativeInterface> mInterface;
XPCNativeMember* mMember;
@ -1281,7 +1281,7 @@ private:
// It represents a new XPCNativeSet we are considering constructing, without
// requiring that the set actually be built.
class XPCNativeSetKey final
class MOZ_STACK_CLASS XPCNativeSetKey final
{
public:
// This represents an existing set |baseSet|.
@ -1314,8 +1314,8 @@ public:
// Allow shallow copy
private:
XPCNativeSet* mBaseSet;
XPCNativeInterface* mAddition;
RefPtr<XPCNativeSet> mBaseSet;
RefPtr<XPCNativeInterface> mAddition;
};
/***************************************************************************/
@ -1324,9 +1324,12 @@ private:
class XPCNativeSet final
{
public:
static XPCNativeSet* GetNewOrUsed(const nsIID* iid);
static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo);
static XPCNativeSet* GetNewOrUsed(XPCNativeSetKey* key);
NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeSet,
DestroyInstance(this))
static already_AddRefed<XPCNativeSet> GetNewOrUsed(const nsIID* iid);
static already_AddRefed<XPCNativeSet> GetNewOrUsed(nsIClassInfo* classInfo);
static already_AddRefed<XPCNativeSet> GetNewOrUsed(XPCNativeSetKey* key);
// This generates a union set.
//
@ -1335,9 +1338,9 @@ class XPCNativeSet final
// algorithm is applied; but if we detect that |secondSet| is a superset of
// |firstSet|, we return |secondSet| without worrying about whether the
// ordering might differ from |firstSet|.
static XPCNativeSet* GetNewOrUsed(XPCNativeSet* firstSet,
XPCNativeSet* secondSet,
bool preserveFirstSetOrder);
static already_AddRefed<XPCNativeSet> GetNewOrUsed(XPCNativeSet* firstSet,
XPCNativeSet* secondSet,
bool preserveFirstSetOrder);
static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
@ -1388,7 +1391,7 @@ class XPCNativeSet final
mMarked = 0;
}
bool IsMarked() const {
return !!mMarked;
return false;
}
#ifdef DEBUG
@ -1397,26 +1400,20 @@ class XPCNativeSet final
void DebugDump(int16_t depth);
static void DestroyInstance(XPCNativeSet* inst);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
protected:
static XPCNativeSet* NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array);
static XPCNativeSet* NewInstanceMutate(XPCNativeSetKey* key);
static already_AddRefed<XPCNativeSet> NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array);
static already_AddRefed<XPCNativeSet> NewInstanceMutate(XPCNativeSetKey* key);
XPCNativeSet()
: mMemberCount(0), mInterfaceCount(0), mMarked(0)
{
MOZ_COUNT_CTOR(XPCNativeSet);
}
~XPCNativeSet() {
for (int i = 0; i < mInterfaceCount; i++) {
NS_RELEASE(mInterfaces[i]);
}
MOZ_COUNT_DTOR(XPCNativeSet);
}
{}
~XPCNativeSet();
void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
static void DestroyInstance(XPCNativeSet* inst);
private:
uint16_t mMemberCount;
uint16_t mInterfaceCount : 15;
@ -1690,7 +1687,7 @@ protected:
// hide ctor
XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
nsIClassInfo* ClassInfo,
XPCNativeSet* Set);
already_AddRefed<XPCNativeSet>&& Set);
bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
bool callPostCreatePrototype);
@ -1704,7 +1701,7 @@ private:
XPCWrappedNativeScope* mScope;
JS::ObjectPtr mJSProtoObject;
nsCOMPtr<nsIClassInfo> mClassInfo;
XPCNativeSet* mSet;
RefPtr<XPCNativeSet> mSet;
XPCNativeScriptableInfo* mScriptableInfo;
};
@ -1856,7 +1853,7 @@ public:
GetSet() const {return mSet;}
void
SetSet(XPCNativeSet* set) {mSet = set;}
SetSet(already_AddRefed<XPCNativeSet> set) {mSet = set;}
static XPCWrappedNative* Get(JSObject* obj) {
MOZ_ASSERT(IS_WN_REFLECTOR(obj));
@ -1999,7 +1996,7 @@ protected:
// This ctor is used if this object will NOT have a proto.
XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
XPCWrappedNativeScope* aScope,
XPCNativeSet* aSet);
already_AddRefed<XPCNativeSet>&& aSet);
virtual ~XPCWrappedNative();
void Destroy();
@ -2035,7 +2032,7 @@ private:
XPCWrappedNativeScope* mMaybeScope;
XPCWrappedNativeProto* mMaybeProto;
};
XPCNativeSet* mSet;
RefPtr<XPCNativeSet> mSet;
JS::TenuredHeap<JSObject*> mFlatJSObject;
XPCNativeScriptableInfo* mScriptableInfo;
XPCWrappedNativeTearOff mFirstTearOff;
@ -2881,7 +2878,6 @@ class TypedAutoMarkingPtr : public AutoMarkingPtr
T* mPtr;
};
typedef TypedAutoMarkingPtr<XPCNativeSet> AutoMarkingNativeSetPtr;
typedef TypedAutoMarkingPtr<XPCWrappedNative> AutoMarkingWrappedNativePtr;
typedef TypedAutoMarkingPtr<XPCWrappedNativeTearOff> AutoMarkingWrappedNativeTearOffPtr;
typedef TypedAutoMarkingPtr<XPCWrappedNativeProto> AutoMarkingWrappedNativeProtoPtr;

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

@ -313,12 +313,12 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
// give the destination object the union of the two native sets. We try
// to do this cleverly in the common case to avoid too much overhead.
XPCWrappedNative* newwn = XPCWrappedNative::Get(obj);
XPCNativeSet* unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
wn->GetSet(), false);
RefPtr<XPCNativeSet> unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
wn->GetSet(), false);
if (!unionSet) {
return;
}
newwn->SetSet(unionSet);
newwn->SetSet(unionSet.forget());
retObj.set(waive ? WaiveXray(cx, obj) : obj);
}