Make cycle collector's purple buffer consist of entries that the objects in the purple buffer can point to, and remove the notion of scan delay (which was previously set to 0). (Bug 490695) r+sr=peterv r=bsmedberg

This commit is contained in:
L. David Baron 2009-05-06 13:46:04 -07:00
Родитель f0c43ecb3d
Коммит 368b7f1ed8
6 изменённых файлов: 421 добавлений и 212 удалений

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

@ -175,8 +175,6 @@ struct nsCycleCollectorParams
PRUint32 mShutdownCollections;
#endif
PRUint32 mScanDelay;
nsCycleCollectorParams() :
#ifdef DEBUG_CC
mDoNothing (PR_GetEnv("XPCOM_CC_DO_NOTHING") != NULL),
@ -186,27 +184,13 @@ struct nsCycleCollectorParams
mFaultIsFatal (PR_GetEnv("XPCOM_CC_FAULT_IS_FATAL") != NULL),
mLogPointers (PR_GetEnv("XPCOM_CC_LOG_POINTERS") != NULL),
mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS),
mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS)
#else
mDoNothing (PR_FALSE),
mDoNothing (PR_FALSE)
#endif
// The default number of collections to "age" candidate
// pointers in the purple buffer before we decide that any
// garbage cycle they're in has stabilized and we want to
// consider scanning it.
//
// Making this number smaller causes:
// - More time to be spent in the collector (bad)
// - Less delay between forming garbage and collecting it (good)
mScanDelay(0)
{
#ifdef DEBUG_CC
char *s = PR_GetEnv("XPCOM_CC_SCAN_DELAY");
if (s)
PR_sscanf(s, "%d", &mScanDelay);
s = PR_GetEnv("XPCOM_CC_SHUTDOWN_COLLECTIONS");
char *s = PR_GetEnv("XPCOM_CC_SHUTDOWN_COLLECTIONS");
if (s)
PR_sscanf(s, "%d", &mShutdownCollections);
#endif
@ -234,11 +218,8 @@ struct nsCycleCollectorStats
PRUint32 mFailedUnlink;
PRUint32 mCollectedNode;
PRUint32 mBumpGeneration;
PRUint32 mZeroGeneration;
PRUint32 mSuspectNode;
PRUint32 mSpills;
PRUint32 mForgetNode;
PRUint32 mFreedWhilePurple;
@ -268,11 +249,8 @@ struct nsCycleCollectorStats
DUMP(mFailedUnlink);
DUMP(mCollectedNode);
DUMP(mBumpGeneration);
DUMP(mZeroGeneration);
DUMP(mSuspectNode);
DUMP(mSpills);
DUMP(mForgetNode);
DUMP(mFreedWhilePurple);
@ -660,8 +638,6 @@ struct GCGraph
// XXX Would be nice to have an nsHashSet<KeyType> API that has
// Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
typedef nsTHashtable<nsVoidPtrHashKey> PointerSet;
typedef nsBaseHashtable<nsVoidPtrHashKey, PRUint32, PRUint32>
PointerSetWithGeneration;
#ifdef DEBUG_CC
static void
@ -670,171 +646,169 @@ WriteGraph(FILE *stream, GCGraph &graph, const void *redPtr);
struct nsPurpleBuffer
{
private:
struct Block {
Block *mNext;
nsPurpleBufferEntry mEntries[128];
#define ASSOCIATIVITY 2
#define INDEX_LOW_BIT 6
#define N_INDEX_BITS 13
#define N_ENTRIES (1 << N_INDEX_BITS)
#define N_POINTERS (N_ENTRIES * ASSOCIATIVITY)
#define TOTAL_BYTES (N_POINTERS * PR_BYTES_PER_WORD)
#define INDEX_MASK PR_BITMASK(N_INDEX_BITS)
#define POINTER_INDEX(P) ((((PRUword)P) >> INDEX_LOW_BIT) & (INDEX_MASK))
#if (INDEX_LOW_BIT + N_INDEX_BITS > (8 * PR_BYTES_PER_WORD))
#error "index bit overflow"
#endif
// This class serves as a generational wrapper around a pldhash
// table: a subset of generation zero lives in mCache, the
// remainder spill into the mBackingStore hashtable. The idea is
// to get a higher hit rate and greater locality of reference for
// generation zero, in which the vast majority of suspect/forget
// calls annihilate one another.
Block() : mNext(nsnull) {}
};
public:
// This class wraps a linked list of the elements in the purple
// buffer.
nsCycleCollectorParams &mParams;
PRUint32 mCount;
Block mFirstBlock;
nsPurpleBufferEntry *mFreeList;
// For objects compiled against Gecko 1.9 and 1.9.1.
PointerSet mCompatObjects;
#ifdef DEBUG_CC
PointerSet mNormalObjects; // duplicates our blocks
nsCycleCollectorStats &mStats;
#endif
void* mCache[N_POINTERS];
PRUint32 mCurrGen;
PointerSetWithGeneration mBackingStore;
#ifdef DEBUG_CC
nsPurpleBuffer(nsCycleCollectorParams &params,
nsCycleCollectorStats &stats)
: mParams(params),
mStats(stats),
mCurrGen(0)
mStats(stats)
{
Init();
InitBlocks();
mNormalObjects.Init();
mCompatObjects.Init();
}
#else
nsPurpleBuffer(nsCycleCollectorParams &params)
: mParams(params),
mCurrGen(0)
: mParams(params)
{
Init();
InitBlocks();
mCompatObjects.Init();
}
#endif
~nsPurpleBuffer()
{
memset(mCache, 0, sizeof(mCache));
mBackingStore.Clear();
FreeBlocks();
}
void Init()
void InitBlocks()
{
memset(mCache, 0, sizeof(mCache));
mBackingStore.Init();
mCount = 0;
mFreeList = nsnull;
StartBlock(&mFirstBlock);
}
void BumpGeneration();
void SelectAgedPointers(GCGraphBuilder &builder);
void StartBlock(Block *aBlock)
{
NS_ABORT_IF_FALSE(!mFreeList, "should not have free list");
// Put all the entries in the block on the free list.
nsPurpleBufferEntry *entries = aBlock->mEntries;
mFreeList = entries;
for (PRUint32 i = 1; i < NS_ARRAY_LENGTH(aBlock->mEntries); ++i) {
entries[i - 1].mNextInFreeList =
(nsPurpleBufferEntry*)(PRUword(entries + i) | 1);
}
entries[NS_ARRAY_LENGTH(aBlock->mEntries) - 1].mNextInFreeList =
(nsPurpleBufferEntry*)1;
}
void FreeBlocks()
{
Block *b = mFirstBlock.mNext;
while (b) {
Block *next = b->mNext;
delete b;
b = next;
}
mFirstBlock.mNext = nsnull;
}
void SelectPointers(GCGraphBuilder &builder);
#ifdef DEBUG_CC
void NoteAll(GCGraphBuilder &builder);
PRBool Exists(void *p) const
{
return mNormalObjects.GetEntry(p) || mCompatObjects.GetEntry(p);
}
#endif
PRBool Exists(void *p)
nsPurpleBufferEntry* NewEntry()
{
PRUint32 idx = POINTER_INDEX(p);
for (PRUint32 i = 0; i < ASSOCIATIVITY; ++i) {
if (mCache[idx+i] == p)
return PR_TRUE;
if (!mFreeList) {
Block *b = new Block;
if (!b) {
return nsnull;
}
StartBlock(b);
// Add the new block as the second block in the list.
b->mNext = mFirstBlock.mNext;
mFirstBlock.mNext = b;
}
PRUint32 gen;
return mBackingStore.Get(p, &gen);
nsPurpleBufferEntry *e = mFreeList;
mFreeList = (nsPurpleBufferEntry*)
(PRUword(mFreeList->mNextInFreeList) & ~PRUword(1));
return e;
}
void Put(void *p)
nsPurpleBufferEntry* Put(nsISupports *p)
{
PRUint32 idx = POINTER_INDEX(p);
for (PRUint32 i = 0; i < ASSOCIATIVITY; ++i) {
if (!mCache[idx+i]) {
mCache[idx+i] = p;
return;
}
nsPurpleBufferEntry *e = NewEntry();
if (!e) {
return nsnull;
}
++mCount;
e->mObject = p;
#ifdef DEBUG_CC
mStats.mSpills++;
mNormalObjects.PutEntry(p);
#endif
SpillOne(p);
// Caller is responsible for filling in result's mRefCnt.
return e;
}
void Remove(void *p)
void Remove(nsPurpleBufferEntry *e)
{
PRUint32 idx = POINTER_INDEX(p);
for (PRUint32 i = 0; i < ASSOCIATIVITY; ++i) {
if (mCache[idx+i] == p) {
mCache[idx+i] = (void*)0;
return;
}
}
mBackingStore.Remove(p);
NS_ASSERTION(mCount != 0, "must have entries");
#ifdef DEBUG_CC
mNormalObjects.RemoveEntry(e->mObject);
#endif
e->mNextInFreeList =
(nsPurpleBufferEntry*)(PRUword(mFreeList) | PRUword(1));
mFreeList = e;
--mCount;
}
void SpillOne(void* &p)
PRBool PutCompatObject(nsISupports *p)
{
mBackingStore.Put(p, mCurrGen);
p = (void*)0;
++mCount;
return !!mCompatObjects.PutEntry(p);
}
void SpillAll()
void RemoveCompatObject(nsISupports *p)
{
for (PRUint32 i = 0; i < N_POINTERS; ++i) {
if (mCache[i]) {
SpillOne(mCache[i]);
}
}
--mCount;
mCompatObjects.RemoveEntry(p);
}
PRUint32 Count()
PRUint32 Count() const
{
PRUint32 count = mBackingStore.Count();
for (PRUint32 i = 0; i < N_POINTERS; ++i) {
if (mCache[i]) {
++count;
}
}
return count;
return mCount;
}
};
static PLDHashOperator
zeroGenerationCallback(const void* ptr,
PRUint32& generation,
void* userArg)
{
#ifdef DEBUG_CC
nsPurpleBuffer *purp = static_cast<nsPurpleBuffer*>(userArg);
purp->mStats.mZeroGeneration++;
#endif
generation = 0;
return PL_DHASH_NEXT;
}
void nsPurpleBuffer::BumpGeneration()
{
SpillAll();
if (mCurrGen == 0xffffffff) {
mBackingStore.Enumerate(zeroGenerationCallback, this);
mCurrGen = 0;
} else {
++mCurrGen;
}
#ifdef DEBUG_CC
mStats.mBumpGeneration++;
#endif
}
static inline PRBool
SufficientlyAged(PRUint32 generation, nsPurpleBuffer *p)
{
return generation + p->mParams.mScanDelay < p->mCurrGen;
}
struct CallbackClosure
{
CallbackClosure(nsPurpleBuffer *aPurpleBuffer, GCGraphBuilder &aBuilder)
@ -850,26 +824,62 @@ static PRBool
AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root);
static PLDHashOperator
ageSelectionCallback(const void* ptr,
PRUint32& generation,
void* userArg)
selectionCallback(nsVoidPtrHashKey* key, void* userArg)
{
CallbackClosure *closure = static_cast<CallbackClosure*>(userArg);
if (SufficientlyAged(generation, closure->mPurpleBuffer) &&
AddPurpleRoot(closure->mBuilder,
static_cast<nsISupports *>(const_cast<void*>(ptr))))
if (AddPurpleRoot(closure->mBuilder,
static_cast<nsISupports *>(
const_cast<void*>(key->GetKey()))))
return PL_DHASH_REMOVE;
return PL_DHASH_NEXT;
}
void
nsPurpleBuffer::SelectAgedPointers(GCGraphBuilder &aBuilder)
nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
{
// Rely on our caller having done a BumpGeneration first, which in
// turn calls SpillAll.
CallbackClosure closure(this, aBuilder);
mBackingStore.Enumerate(ageSelectionCallback, &closure);
#ifdef DEBUG_CC
NS_ABORT_IF_FALSE(mCompatObjects.Count() + mNormalObjects.Count() ==
mCount,
"count out of sync");
#endif
if (mCompatObjects.Count()) {
mCount -= mCompatObjects.Count();
CallbackClosure closure(this, aBuilder);
mCompatObjects.EnumerateEntries(selectionCallback, &closure);
mCount += mCompatObjects.Count(); // in case of allocation failure
}
// Walk through all the blocks.
for (Block *b = &mFirstBlock; b; b = b->mNext) {
for (nsPurpleBufferEntry *e = b->mEntries,
*eEnd = e + NS_ARRAY_LENGTH(b->mEntries);
e != eEnd; ++e) {
if (!(PRUword(e->mObject) & PRUword(1))) {
// This is a real entry (rather than something on the
// free list).
if (AddPurpleRoot(aBuilder, e->mObject)) {
#ifdef DEBUG_CC
mNormalObjects.RemoveEntry(e->mObject);
#endif
--mCount;
// Put this entry on the free list in case some
// call to AddPurpleRoot fails and we don't rebuild
// the free list below.
e->mNextInFreeList = (nsPurpleBufferEntry*)
(PRUword(mFreeList) | PRUword(1));
mFreeList = e;
}
}
}
}
NS_WARN_IF_FALSE(mCount == 0, "AddPurpleRoot failed");
if (mCount == 0) {
FreeBlocks();
InitBlocks();
}
}
@ -931,8 +941,13 @@ struct nsCycleCollector
nsCycleCollector();
~nsCycleCollector();
// The first pair of Suspect and Forget functions are only used by
// old XPCOM binary components.
PRBool Suspect(nsISupports *n);
PRBool Forget(nsISupports *n);
nsPurpleBufferEntry* Suspect2(nsISupports *n);
PRBool Forget2(nsPurpleBufferEntry *e);
PRUint32 Collect(PRUint32 aTryCollections = 1);
PRBool BeginCollection();
PRBool FinishCollection();
@ -1506,26 +1521,35 @@ AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root)
#ifdef DEBUG_CC
static PLDHashOperator
noteAllCallback(const void* ptr, PRUint32& generation, void* userArg)
noteAllCallback(nsVoidPtrHashKey* key, void* userArg)
{
GCGraphBuilder *builder = static_cast<GCGraphBuilder*>(userArg);
builder->NoteXPCOMRoot(static_cast<nsISupports *>(const_cast<void*>(ptr)));
builder->NoteXPCOMRoot(
static_cast<nsISupports *>(const_cast<void*>(key->GetKey())));
return PL_DHASH_NEXT;
}
void
nsPurpleBuffer::NoteAll(GCGraphBuilder &builder)
{
SpillAll();
mBackingStore.Enumerate(noteAllCallback, &builder);
mCompatObjects.EnumerateEntries(noteAllCallback, &builder);
for (Block *b = &mFirstBlock; b; b = b->mNext) {
for (nsPurpleBufferEntry *e = b->mEntries,
*eEnd = e + NS_ARRAY_LENGTH(b->mEntries);
e != eEnd; ++e) {
if (!(PRUword(e->mObject) & PRUword(1))) {
builder.NoteXPCOMRoot(e->mObject);
}
}
}
}
#endif
void
nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
{
mPurpleBuf.BumpGeneration();
mPurpleBuf.SelectAgedPointers(builder);
mPurpleBuf.SelectPointers(builder);
}
void
@ -2168,9 +2192,7 @@ nsCycleCollector::Suspect(nsISupports *n)
}
#endif
mPurpleBuf.Put(n);
return PR_TRUE;
return mPurpleBuf.PutCompatObject(n);
}
@ -2204,7 +2226,78 @@ nsCycleCollector::Forget(nsISupports *n)
}
#endif
mPurpleBuf.Remove(n);
mPurpleBuf.RemoveCompatObject(n);
return PR_TRUE;
}
nsPurpleBufferEntry*
nsCycleCollector::Suspect2(nsISupports *n)
{
// Re-entering ::Suspect during collection used to be a fault, but
// we are canonicalizing nsISupports pointers using QI, so we will
// see some spurious refcount traffic here.
if (mScanInProgress)
return nsnull;
NS_ASSERTION(nsCycleCollector_isScanSafe(n),
"suspected a non-scansafe pointer");
NS_ASSERTION(NS_IsMainThread(), "trying to suspect from non-main thread");
if (mParams.mDoNothing)
return nsnull;
#ifdef DEBUG_CC
mStats.mSuspectNode++;
if (nsCycleCollector_shouldSuppress(n))
return nsnull;
#ifndef __MINGW32__
if (mParams.mHookMalloc)
InitMemHook();
#endif
if (mParams.mLogPointers) {
if (!mPtrLog)
mPtrLog = fopen("pointer_log", "w");
fprintf(mPtrLog, "S %p\n", static_cast<void*>(n));
}
#endif
// Caller is responsible for filling in result's mRefCnt.
return mPurpleBuf.Put(n);
}
PRBool
nsCycleCollector::Forget2(nsPurpleBufferEntry *e)
{
// Re-entering ::Forget during collection used to be a fault, but
// we are canonicalizing nsISupports pointers using QI, so we will
// see some spurious refcount traffic here.
if (mScanInProgress)
return PR_FALSE;
NS_ASSERTION(NS_IsMainThread(), "trying to forget from non-main thread");
#ifdef DEBUG_CC
mStats.mForgetNode++;
#ifndef __MINGW32__
if (mParams.mHookMalloc)
InitMemHook();
#endif
if (mParams.mLogPointers) {
if (!mPtrLog)
mPtrLog = fopen("pointer_log", "w");
fprintf(mPtrLog, "F %p\n", static_cast<void*>(e->mObject));
}
#endif
mPurpleBuf.Remove(e);
return PR_TRUE;
}
@ -2228,7 +2321,6 @@ nsCycleCollector::Freed(void *n)
mStats.mForgetNode++;
mStats.mFreedWhilePurple++;
Fault("freed while purple", n);
mPurpleBuf.Remove(n);
if (mParams.mLogPointers) {
if (!mPtrLog)
@ -2492,11 +2584,9 @@ nsCycleCollector::SuspectedCount()
void
nsCycleCollector::Shutdown()
{
// Here we want to run a final collection on everything we've seen
// buffered, irrespective of age; then permanently disable
// the collector because the program is shutting down.
// Here we want to run a final collection and then permanently
// disable the collector because the program is shutting down.
mParams.mScanDelay = 0;
Collect(SHUTDOWN_COLLECTIONS(mParams));
#ifdef DEBUG_CC
@ -2798,7 +2888,9 @@ nsCycleCollector::ExplainLiveExpectedGarbage()
while (!queue.IsDone()) {
PtrInfo *pi = queue.GetNext();
if (pi->mColor == white) {
if (mPurpleBuf.Exists(pi->mPointer)) {
if (pi->mLangID ==
nsIProgrammingLanguage::CPLUSPLUS &&
mPurpleBuf.Exists(pi->mPointer)) {
printf(
"nsCycleCollector: %s %p in component %d\n"
" which was reference counted during the root/unlink/unroot phase of the\n"
@ -2933,13 +3025,26 @@ NS_CycleCollectorSuspect(nsISupports *n)
return PR_FALSE;
}
PRBool
NS_CycleCollectorForget(nsISupports *n)
{
return sCollector ? sCollector->Forget(n) : PR_TRUE;
}
nsPurpleBufferEntry*
NS_CycleCollectorSuspect2(nsISupports *n)
{
if (sCollector)
return sCollector->Suspect2(n);
return nsnull;
}
PRBool
NS_CycleCollectorForget2(nsPurpleBufferEntry *e)
{
return sCollector ? sCollector->Forget2(e) : PR_TRUE;
}
PRUint32
nsCycleCollector_collect()

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

@ -66,6 +66,8 @@
# define NS_LogCOMPtrRelease NS_LogCOMPtrRelease_P
# define NS_CycleCollectorSuspect NS_CycleCollectorSuspect_P
# define NS_CycleCollectorForget NS_CycleCollectorForget_P
# define NS_CycleCollectorSuspect2 NS_CycleCollectorSuspect2_P
# define NS_CycleCollectorForget2 NS_CycleCollectorForget2_P
#endif
#include "nscore.h"
@ -73,8 +75,10 @@
#ifdef __cplusplus
#define DECL_CLASS(c) class c
#define DECL_STRUCT(c) struct c
#else
#define DECL_CLASS(c) typedef struct c c
#define DECL_STRUCT(c) typedef struct c c
#endif
DECL_CLASS(nsAString);
@ -91,6 +95,7 @@ DECL_CLASS(nsIDirectoryServiceProvider);
DECL_CLASS(nsIMemory);
DECL_CLASS(nsIDebug);
DECL_CLASS(nsITraceRefcnt);
DECL_STRUCT(nsPurpleBufferEntry);
/**
* Every XPCOM component implements this function signature, which is the
@ -459,6 +464,9 @@ NS_LogCOMPtrRelease(void *aCOMPtr, nsISupports *aObject);
* The XPCOM cycle collector analyzes and breaks reference cycles between
* participating XPCOM objects. All objects in the cycle must implement
* nsCycleCollectionParticipant to break cycles correctly.
*
* The first two functions below exist only to support binary components
* that were compiled for older XPCOM versions.
*/
XPCOM_API(PRBool)
NS_CycleCollectorSuspect(nsISupports *n);
@ -466,6 +474,12 @@ NS_CycleCollectorSuspect(nsISupports *n);
XPCOM_API(PRBool)
NS_CycleCollectorForget(nsISupports *n);
XPCOM_API(nsPurpleBufferEntry*)
NS_CycleCollectorSuspect2(nsISupports *n);
XPCOM_API(PRBool)
NS_CycleCollectorForget2(nsPurpleBufferEntry *e);
/**
* Categories (in the category manager service) used by XPCOM:
*/

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

@ -119,6 +119,9 @@ typedef nsresult (* GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*, nsISomeInterf
typedef void (* DestroyXPTCallStubFunc)(nsISomeInterface*);
typedef nsresult (* InvokeByIndexFunc)(nsISupports*, PRUint32, PRUint32, nsXPTCVariant*);
typedef PRBool (* CycleCollectorFunc)(nsISupports*);
typedef nsPurpleBufferEntry*
(* CycleCollectorSuspect2Func)(nsISupports*);
typedef PRBool (* CycleCollectorForget2Func)(nsPurpleBufferEntry*);
// PRIVATE AND DEPRECATED
typedef NS_CALLBACK(XPCOMExitRoutine)(void);
@ -194,6 +197,10 @@ typedef struct XPCOMFunctions{
CStringSetIsVoidFunc cstringSetIsVoid;
CStringGetIsVoidFunc cstringGetIsVoid;
// Added for Mozilla 1.9.2
CycleCollectorSuspect2Func cycleSuspect2Func;
CycleCollectorForget2Func cycleForget2Func;
} XPCOMFunctions;
typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions *entryPoints, const char* libraryPath);

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

@ -85,32 +85,51 @@ private:
#endif // NS_DEBUG
#define NS_PURPLE_BIT ((PRUint32)(1 << 31))
#define NS_PURPLE_MASK (~NS_PURPLE_BIT)
#define NS_PURPLE_BIT_SET(x) ((x) & (NS_PURPLE_BIT))
#define NS_CLEAR_PURPLE_BIT(x) ((x) &= (NS_PURPLE_MASK))
#define NS_VALUE_WITHOUT_PURPLE_BIT(x) ((x) & (NS_PURPLE_MASK))
#define NS_CCAR_REFCNT_BIT 1
#define NS_CCAR_REFCNT_TO_TAGGED(rc_) \
NS_INT32_TO_PTR((rc_ << 1) | NS_CCAR_REFCNT_BIT)
#define NS_CCAR_PURPLE_ENTRY_TO_TAGGED(pe_) \
static_cast<void*>(pe_)
#define NS_CCAR_TAGGED_TO_REFCNT(tagged_) \
nsrefcnt(NS_PTR_TO_INT32(tagged_) >> 1)
#define NS_CCAR_TAGGED_TO_PURPLE_ENTRY(tagged_) \
static_cast<nsPurpleBufferEntry*>(tagged_)
#define NS_CCAR_TAGGED_STABILIZED_REFCNT NS_CCAR_PURPLE_ENTRY_TO_TAGGED(0)
// Support for ISupports classes which interact with cycle collector.
/**
* This struct (once shipped) will be FROZEN with respect to the
* NS_CycleCollectorSuspect2 and NS_CycleCollectorForget2 functions. If
* we need to change the struct, we'll need Suspect3 and Forget3 for the
* new versions.
*/
struct nsPurpleBufferEntry {
union {
nsISupports *mObject; // when low bit unset
nsPurpleBufferEntry *mNextInFreeList; // when low bit set
};
// When an object is in the purple buffer, it replaces its reference
// count with a (tagged) pointer to this entry, so we store the
// reference count for it.
nsrefcnt mRefCnt;
};
class nsCycleCollectingAutoRefCnt {
public:
nsCycleCollectingAutoRefCnt()
: mValue(0)
: mTagged(NS_CCAR_REFCNT_TO_TAGGED(0))
{}
nsCycleCollectingAutoRefCnt(nsrefcnt aValue)
: mValue(aValue)
: mTagged(NS_CCAR_REFCNT_TO_TAGGED(aValue))
{
NS_CLEAR_PURPLE_BIT(mValue);
}
nsrefcnt incr(nsISupports *owner)
{
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT)) {
if (NS_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) {
// The sentinel value "purple bit alone, refcount 0" means
// that we're stabilized, during finalization. In this
// state we lie about our actual refcount if anyone asks
@ -120,66 +139,94 @@ public:
return 2;
}
nsrefcnt tmp = get();
PRBool purple = static_cast<PRBool>(NS_PURPLE_BIT_SET(mValue));
nsrefcnt refcount;
if (IsPurple()) {
nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged);
NS_ASSERTION(e->mObject == owner, "wrong entry");
refcount = e->mRefCnt;
NS_ASSERTION(refcount != 0, "purple ISupports pointer with zero refcnt");
if (NS_UNLIKELY(purple)) {
NS_ASSERTION(tmp != 0, "purple ISupports pointer with zero refcnt");
if (!NS_CycleCollectorForget(owner))
tmp |= NS_PURPLE_BIT;
if (NS_LIKELY(NS_CycleCollectorForget2(e))) {
// |e| is now invalid
++refcount;
mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
} else {
++refcount;
e->mRefCnt = refcount;
}
} else {
refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged);
++refcount;
mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
}
mValue = tmp + 1;
return mValue;
return refcount;
}
void stabilizeForDeletion(nsISupports *owner)
{
mValue = NS_PURPLE_BIT;
mTagged = NS_CCAR_TAGGED_STABILIZED_REFCNT;
}
nsrefcnt decr(nsISupports *owner)
{
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
if (NS_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT))
return 1;
nsrefcnt tmp = get();
NS_ASSERTION(tmp >= 1, "decr() called with zero refcnt");
nsrefcnt refcount;
if (IsPurple()) {
nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged);
NS_ASSERTION(e->mObject == owner, "wrong entry");
refcount = e->mRefCnt;
--refcount;
if (NS_UNLIKELY(refcount == 0)) {
if (NS_UNLIKELY(!NS_CycleCollectorForget2(e))) {
NS_NOTREACHED("forget should not fail when reference count hits 0");
}
mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
} else {
e->mRefCnt = refcount;
}
} else {
refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged);
--refcount;
PRBool purple = static_cast<PRBool>(NS_PURPLE_BIT_SET(mValue));
PRBool shouldBePurple = tmp > 1;
if (NS_UNLIKELY(shouldBePurple && !purple)) {
if (!NS_CycleCollectorSuspect(owner))
shouldBePurple = PR_FALSE;
} else if (NS_UNLIKELY(tmp == 1 && purple)) {
if (!NS_CycleCollectorForget(owner)) {
NS_NOTREACHED("forget should not fail when reference count hits 0");
nsPurpleBufferEntry *e;
if (NS_LIKELY(refcount > 0) &&
((e = NS_CycleCollectorSuspect2(owner)))) {
e->mRefCnt = refcount;
mTagged = NS_CCAR_PURPLE_ENTRY_TO_TAGGED(e);
} else {
mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
}
}
--tmp;
if (shouldBePurple)
mValue = tmp | NS_PURPLE_BIT;
else
mValue = tmp;
return tmp;
return refcount;
}
void unmarkPurple()
{
if (NS_LIKELY(mValue != NS_PURPLE_BIT))
NS_CLEAR_PURPLE_BIT(mValue);
NS_ASSERTION(IsPurple(), "must be purple");
nsrefcnt refcount = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt;
mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
}
PRBool IsPurple() const
{
NS_ASSERTION(mTagged != NS_CCAR_TAGGED_STABILIZED_REFCNT,
"should have checked for stabilization first");
return !(NS_PTR_TO_INT32(mTagged) & NS_CCAR_REFCNT_BIT);
}
nsrefcnt get() const
{
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
if (NS_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT))
return 1;
return NS_VALUE_WITHOUT_PURPLE_BIT(mValue);
return NS_UNLIKELY(IsPurple())
? NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt
: NS_CCAR_TAGGED_TO_REFCNT(mTagged);
}
operator nsrefcnt() const
@ -188,7 +235,7 @@ public:
}
private:
nsrefcnt mValue;
void *mTagged;
};
class nsAutoRefCnt {

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

@ -565,3 +565,21 @@ NS_CycleCollectorForget(nsISupports* obj)
return xpcomFunctions.cycleForgetFunc(obj);
}
XPCOM_API(nsPurpleBufferEntry*)
NS_CycleCollectorSuspect2(nsISupports* obj)
{
if (!xpcomFunctions.cycleSuspect2Func)
return nsnull;
return xpcomFunctions.cycleSuspect2Func(obj);
}
XPCOM_API(PRBool)
NS_CycleCollectorForget2(nsPurpleBufferEntry* e)
{
if (!xpcomFunctions.cycleForget2Func)
return PR_FALSE;
return xpcomFunctions.cycleForget2Func(e);
}

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

@ -120,7 +120,11 @@ static const XPCOMFunctions kFrozenFunctions = {
&NS_StringSetIsVoid_P,
&NS_StringGetIsVoid_P,
&NS_CStringSetIsVoid_P,
&NS_CStringGetIsVoid_P
&NS_CStringGetIsVoid_P,
// these functions were added post 1.9.1
&NS_CycleCollectorSuspect2_P,
&NS_CycleCollectorForget2_P
};
EXPORT_XPCOM_API(nsresult)
@ -553,3 +557,17 @@ NS_CycleCollectorForget(nsISupports* obj)
{
return NS_CycleCollectorForget_P(obj);
}
#undef NS_CycleCollectorSuspect2
EXPORT_XPCOM_API(nsPurpleBufferEntry*)
NS_CycleCollectorSuspect2(nsISupports* obj)
{
return NS_CycleCollectorSuspect2_P(obj);
}
#undef NS_CycleCollectorForget2
EXPORT_XPCOM_API(PRBool)
NS_CycleCollectorForget2(nsPurpleBufferEntry* e)
{
return NS_CycleCollectorForget2_P(e);
}