Bug 937818, part 1 - Add objects to the purple buffer on AddRef. r=smaug

ICC uses this to track objects that have been AddRef'd during ICC graph building.
For those objects, we may not have the proper information for them, so treat them
as live.
This commit is contained in:
Andrew McCreight 2013-12-17 19:29:57 -08:00
parent 197a13c1ee
commit 5bbea5ab96
4 changed files with 50 additions and 9 deletions

View File

@ -225,7 +225,8 @@ nsXPCWrappedJS::AddRef(void)
MOZ_CRASH();
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
nsrefcnt cnt = mRefCnt.incr();
nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
nsrefcnt cnt = mRefCnt.incr(base);
NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
if (2 == cnt && IsValid()) {
@ -254,7 +255,7 @@ nsXPCWrappedJS::Release(void)
mRefCnt.stabilizeForDeletion();
DeleteCycleCollectable();
} else {
mRefCnt.incr();
mRefCnt.incr(base);
Destroy();
mRefCnt.decr(base);
}

View File

@ -144,7 +144,7 @@ _class::Internal::AddRef(void) \
_class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
nsrefcnt count = agg->mRefCnt.incr(); \
nsrefcnt count = agg->mRefCnt.incr(this); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \
return count; \
} \

View File

@ -71,7 +71,30 @@
//
// The second problem is that objects in the graph can be changed, say by
// being addrefed or released, or by having a field updated, after the object
// has been added to the graph. This will be addressed in bug 937818.
// has been added to the graph. The problem is that ICC can miss a newly
// created reference to an object, and end up unlinking an object that is
// actually alive.
//
// The basic idea of the solution, from "An on-the-fly Reference Counting
// Garbage Collector for Java" by Levanoni and Petrank, is to notice if an
// object has had an additional reference to it created during the collection,
// and if so, don't collect it during the current collection. This avoids having
// to rerun the scan as in Bacon & Rajan 2001.
//
// For cycle collected C++ objects, we modify AddRef to place the object in
// the purple buffer, in addition to Release. Then, in the CC, we treat any
// objects in the purple buffer as being alive, after graph building has
// completed. Because they are in the purple buffer, they will be suspected
// in the next CC, so there's no danger of leaks. This is imprecise, because
// we will treat as live an object that has been Released but not AddRefed
// during graph building, but that's probably rare enough that the additional
// bookkeeping overhead is not worthwhile.
//
// For JS objects, the cycle collector is only looking at gray objects. If a
// gray object is touched during ICC, it will be made black by UnmarkGray.
// Thus, if a JS object has become black during the ICC, we treat it as live.
// Merged JS zones have to be handled specially: we scan all zone globals.
// If any are black, we treat the zone as being black.
// Safety

View File

@ -92,10 +92,23 @@ public:
{
}
MOZ_ALWAYS_INLINE uintptr_t incr()
MOZ_ALWAYS_INLINE uintptr_t incr(nsISupports *owner)
{
return incr(owner, nullptr);
}
MOZ_ALWAYS_INLINE uintptr_t incr(void *owner, nsCycleCollectionParticipant *p)
{
mRefCntAndFlags += NS_REFCOUNT_CHANGE;
mRefCntAndFlags &= ~NS_IS_PURPLE;
// For incremental cycle collection, use the purple buffer to track objects
// that have been AddRef'd.
if (!IsInPurpleBuffer()) {
mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
// Refcount isn't zero, so Suspect won't delete anything.
MOZ_ASSERT(get() > 0);
NS_CycleCollectorSuspect3(owner, p, this, nullptr);
}
return NS_REFCOUNT_VALUE(mRefCntAndFlags);
}
@ -265,7 +278,9 @@ public:
#define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
nsrefcnt count = mRefCnt.incr(); \
nsrefcnt count = \
mRefCnt.incr(static_cast<void*>(this), \
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
return count;
@ -296,7 +311,8 @@ NS_METHOD_(nsrefcnt) _class::Release(void)
&shouldDelete); \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
mRefCnt.incr(); \
mRefCnt.incr(static_cast<void*>(this), \
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
_last; \
mRefCnt.decr(static_cast<void*>(this), \
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
@ -502,7 +518,8 @@ NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void) \
{ \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
nsrefcnt count = mRefCnt.incr(); \
nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
nsrefcnt count = mRefCnt.incr(base); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
return count; \
}
@ -537,7 +554,7 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
mRefCnt.incr(); \
mRefCnt.incr(base); \
_last; \
mRefCnt.decr(base); \
if (shouldDelete) { \