Fix for bug 375075 (Stop leaking the cycle collector on shutdown). r=bsmedberg, sr=dbaron.

This commit is contained in:
peterv@propagandism.org 2007-03-27 02:49:06 -07:00
parent 69336f5dcb
commit 12431cf555
4 changed files with 204 additions and 206 deletions

View File

@ -135,6 +135,7 @@
#include "plstr.h" #include "plstr.h"
#include "prtime.h" #include "prtime.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "nsTArray.h"
#include <stdio.h> #include <stdio.h>
#ifdef WIN32 #ifdef WIN32
@ -256,6 +257,9 @@ struct nsCycleCollectorStats
} }
}; };
static void
ToParticipant(nsISupports *s, nsCycleCollectionParticipant **cp);
static PRBool static PRBool
nsCycleCollector_shouldSuppress(nsISupports *s); nsCycleCollector_shouldSuppress(nsISupports *s);
@ -459,15 +463,99 @@ nsPurpleBuffer::SelectAgedPointers(nsDeque *transferBuffer)
////////////////////////////////////////////////////////////////////////
// Implement the LanguageRuntime interface for C++/XPCOM
////////////////////////////////////////////////////////////////////////
struct nsCycleCollectionXPCOMRuntime :
public nsCycleCollectionLanguageRuntime
{
nsresult BeginCycleCollection()
{
return NS_OK;
}
nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
{
nsresult rv;
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
nsCycleCollectionParticipant *cp;
ToParticipant(s, &cp);
if (!cp) {
Fault("walking wrong type of pointer", s);
return NS_ERROR_FAILURE;
}
rv = cp->Traverse(s, cb);
if (NS_FAILED(rv)) {
Fault("XPCOM pointer traversal failed", s);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult Root(const nsDeque &nodes)
{
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
NS_ADDREF(s);
}
return NS_OK;
}
nsresult Unlink(const nsDeque &nodes)
{
nsresult rv;
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
nsCycleCollectionParticipant *cp;
ToParticipant(s, &cp);
if (!cp) {
Fault("unlinking wrong kind of pointer", s);
return NS_ERROR_FAILURE;
}
rv = cp->Unlink(s);
if (NS_FAILED(rv)) {
Fault("failed unlink", s);
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
nsresult Unroot(const nsDeque &nodes)
{
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
NS_RELEASE(s);
}
return NS_OK;
}
nsresult FinishCycleCollection()
{
return NS_OK;
}
};
struct nsCycleCollector struct nsCycleCollector
{ {
PRUint32 mTick;
PRTime mLastAging;
PRBool mCollectionInProgress; PRBool mCollectionInProgress;
PRBool mScanInProgress; PRBool mScanInProgress;
GCTable mGraph; GCTable mGraph;
nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1]; nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
// The set of buffers |mBufs| serves a variety of purposes; mostly // The set of buffers |mBufs| serves a variety of purposes; mostly
// involving the transfer of pointers from a hashtable iterator // involving the transfer of pointers from a hashtable iterator
@ -476,7 +564,7 @@ struct nsCycleCollector
// set-of-all-pointers); in other contexts, one buffer is used // set-of-all-pointers); in other contexts, one buffer is used
// per-language (as a set-of-pointers-in-language-N). // per-language (as a set-of-pointers-in-language-N).
nsDeque *mBufs[nsIProgrammingLanguage::MAX+1]; nsDeque mBufs[nsIProgrammingLanguage::MAX + 1];
nsCycleCollectorParams mParams; nsCycleCollectorParams mParams;
nsCycleCollectorStats mStats; nsCycleCollectorStats mStats;
@ -563,17 +651,6 @@ static nsCycleCollector *sCollector = nsnull;
// Utility functions // Utility functions
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
static inline nsCycleCollector*
getCollector()
{
if (!sCollector)
sCollector = new nsCycleCollector;
return sCollector;
}
struct safetyCallback : struct safetyCallback :
public nsCycleCollectionTraversalCallback public nsCycleCollectionTraversalCallback
{ {
@ -602,9 +679,11 @@ EnsurePtrInfo(GCTable & tab, void *n, PtrInfo & pi)
static void static void
Fault(const char *msg, const void *ptr=nsnull) Fault(const char *msg, const void *ptr=nsnull)
{ {
nsCycleCollector* cc = getCollector(); // This should be nearly impossible, but just in case.
if (!sCollector)
return;
if (cc->mParams.mFaultIsFatal) { if (sCollector->mParams.mFaultIsFatal) {
if (ptr) if (ptr)
printf("Fatal fault in cycle collector: %s (ptr: %p)\n", msg, ptr); printf("Fatal fault in cycle collector: %s (ptr: %p)\n", msg, ptr);
@ -626,24 +705,22 @@ Fault(const char *msg, const void *ptr=nsnull)
// probably a better user experience than crashing. Besides, we // probably a better user experience than crashing. Besides, we
// *should* never hit a fault. // *should* never hit a fault.
cc->mParams.mDoNothing = PR_TRUE; sCollector->mParams.mDoNothing = PR_TRUE;
} }
void void
GraphWalker::DescribeNode(size_t refCount, size_t objSz, const char *objName) GraphWalker::DescribeNode(size_t refCount, size_t objSz, const char *objName)
{ {
nsCycleCollector* cc = getCollector();
if (refCount == 0) if (refCount == 0)
Fault("zero refcount", mCurrPtr); Fault("zero refcount", mCurrPtr);
mCurrPi.mBytes = objSz; mCurrPi.mBytes = objSz;
mCurrPi.mName = objName; mCurrPi.mName = objName;
this->VisitNode(mCurrPtr, mCurrPi, refCount); this->VisitNode(mCurrPtr, mCurrPi, refCount);
cc->mStats.mVisitedNode++; sCollector->mStats.mVisitedNode++;
if (mCurrPi.mLang == nsIProgrammingLanguage::JAVASCRIPT) if (mCurrPi.mLang == nsIProgrammingLanguage::JAVASCRIPT)
cc->mStats.mVisitedJSNode++; sCollector->mStats.mVisitedJSNode++;
} }
@ -728,7 +805,7 @@ GraphWalker::Walk(void *s0)
} }
} }
} }
getCollector()->mStats.mWalkedGraph++; sCollector->mStats.mWalkedGraph++;
} }
@ -753,7 +830,7 @@ struct MarkGreyWalker : public GraphWalker
{ {
pi.mColor = grey; pi.mColor = grey;
pi.mRefCount = refcount; pi.mRefCount = refcount;
getCollector()->mStats.mSetColorGrey++; sCollector->mStats.mSetColorGrey++;
mGraph.Put(p, pi); mGraph.Put(p, pi);
} }
@ -767,16 +844,16 @@ struct MarkGreyWalker : public GraphWalker
void void
nsCycleCollector::CollectPurple() nsCycleCollector::CollectPurple()
{ {
mPurpleBuf.SelectAgedPointers(mBufs[0]); mPurpleBuf.SelectAgedPointers(&mBufs[0]);
} }
void void
nsCycleCollector::MarkRoots() nsCycleCollector::MarkRoots()
{ {
int i; int i;
for (i = 0; i < mBufs[0]->GetSize(); ++i) { for (i = 0; i < mBufs[0].GetSize(); ++i) {
PtrInfo pi; PtrInfo pi;
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->ObjectAt(i)); nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
s = canonicalize(s); s = canonicalize(s);
EnsurePtrInfo(mGraph, s, pi); EnsurePtrInfo(mGraph, s, pi);
MarkGreyWalker(mGraph, mRuntimes).Walk(s); MarkGreyWalker(mGraph, mRuntimes).Walk(s);
@ -804,7 +881,7 @@ struct ScanBlackWalker : public GraphWalker
void VisitNode(void *p, PtrInfo & pi, size_t refcount) void VisitNode(void *p, PtrInfo & pi, size_t refcount)
{ {
pi.mColor = black; pi.mColor = black;
getCollector()->mStats.mSetColorBlack++; sCollector->mStats.mSetColorBlack++;
mGraph.Put(p, pi); mGraph.Put(p, pi);
} }
@ -826,8 +903,6 @@ struct scanWalker : public GraphWalker
void VisitNode(void *p, PtrInfo & pi, size_t refcount) void VisitNode(void *p, PtrInfo & pi, size_t refcount)
{ {
nsCycleCollector* cc = getCollector();
if (pi.mColor != grey) if (pi.mColor != grey)
Fault("scanning non-grey node", p); Fault("scanning non-grey node", p);
@ -836,11 +911,11 @@ struct scanWalker : public GraphWalker
if (pi.mInternalRefs == refcount) { if (pi.mInternalRefs == refcount) {
pi.mColor = white; pi.mColor = white;
cc->mStats.mSetColorWhite++; sCollector->mStats.mSetColorWhite++;
} else { } else {
ScanBlackWalker(mGraph, mRuntimes).Walk(p); ScanBlackWalker(mGraph, mRuntimes).Walk(p);
pi.mColor = black; pi.mColor = black;
cc->mStats.mSetColorBlack++; sCollector->mStats.mSetColorBlack++;
} }
mGraph.Put(p, pi); mGraph.Put(p, pi);
} }
@ -864,8 +939,8 @@ nsCycleCollector::ScanRoots()
{ {
int i; int i;
for (i = 0; i < mBufs[0]->GetSize(); ++i) { for (i = 0; i < mBufs[0].GetSize(); ++i) {
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->ObjectAt(i)); nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
s = canonicalize(s); s = canonicalize(s);
scanWalker(mGraph, mRuntimes).Walk(s); scanWalker(mGraph, mRuntimes).Walk(s);
} }
@ -896,7 +971,7 @@ FindWhiteCallback(const void* ptr,
if (pinfo.mLang > nsIProgrammingLanguage::MAX) if (pinfo.mLang > nsIProgrammingLanguage::MAX)
Fault("White node has bad language ID", p); Fault("White node has bad language ID", p);
else else
collector->mBufs[pinfo.mLang]->Push(p); collector->mBufs[pinfo.mLang].Push(p);
if (pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS) { if (pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS) {
nsISupports* s = NS_STATIC_CAST(nsISupports*, p); nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
@ -936,7 +1011,7 @@ nsCycleCollector::CollectWhite()
nsresult rv; nsresult rv;
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
mBufs[i]->Empty(); mBufs[i].Empty();
#ifndef __MINGW32__ #ifndef __MINGW32__
#ifdef WIN32 #ifdef WIN32
@ -949,8 +1024,8 @@ nsCycleCollector::CollectWhite()
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) { for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
if (mRuntimes[i] && if (mRuntimes[i] &&
mBufs[i]->GetSize() > 0) { mBufs[i].GetSize() > 0) {
rv = mRuntimes[i]->Root(*mBufs[i]); rv = mRuntimes[i]->Root(mBufs[i]);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
Fault("Failed root call while unlinking"); Fault("Failed root call while unlinking");
} }
@ -958,28 +1033,28 @@ nsCycleCollector::CollectWhite()
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) { for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
if (mRuntimes[i] && if (mRuntimes[i] &&
mBufs[i]->GetSize() > 0) { mBufs[i].GetSize() > 0) {
rv = mRuntimes[i]->Unlink(*mBufs[i]); rv = mRuntimes[i]->Unlink(mBufs[i]);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
Fault("Failed unlink call while unlinking"); Fault("Failed unlink call while unlinking");
mStats.mFailedUnlink++; mStats.mFailedUnlink++;
} else { } else {
mStats.mCollectedNode += mBufs[i]->GetSize(); mStats.mCollectedNode += mBufs[i].GetSize();
} }
} }
} }
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) { for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
if (mRuntimes[i] && if (mRuntimes[i] &&
mBufs[i]->GetSize() > 0) { mBufs[i].GetSize() > 0) {
rv = mRuntimes[i]->Unroot(*mBufs[i]); rv = mRuntimes[i]->Unroot(mBufs[i]);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
Fault("Failed unroot call while unlinking"); Fault("Failed unroot call while unlinking");
} }
} }
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
mBufs[i]->Empty(); mBufs[i].Empty();
#ifndef __MINGW32__ #ifndef __MINGW32__
#ifdef WIN32 #ifdef WIN32
@ -991,98 +1066,6 @@ nsCycleCollector::CollectWhite()
} }
////////////////////////////////////////////////////////////////////////
// Implement the LanguageRuntime interface for C++/XPCOM
////////////////////////////////////////////////////////////////////////
struct nsCycleCollectionXPCOMRuntime :
public nsCycleCollectionLanguageRuntime
{
nsresult BeginCycleCollection()
{
return NS_OK;
}
nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
{
nsresult rv;
// We use QI to move from an nsISupports to an
// nsCycleCollectionParticipant, which is a per-class
// singleton helper object that implements traversal and
// unlinking logic for the nsISupports in question.
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
nsCOMPtr<nsCycleCollectionParticipant> cp = do_QueryInterface(s, &rv);
if (NS_FAILED(rv)) {
Fault("walking wrong type of pointer", s);
return NS_ERROR_FAILURE;
}
getCollector()->mStats.mSuccessfulQI++;
rv = cp->Traverse(s, cb);
if (NS_FAILED(rv)) {
Fault("XPCOM pointer traversal failed", s);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult Root(const nsDeque &nodes)
{
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
NS_ADDREF(s);
}
return NS_OK;
}
nsresult Unlink(const nsDeque &nodes)
{
nsresult rv;
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
nsCOMPtr<nsCycleCollectionParticipant> cp
= do_QueryInterface(s, &rv);
if (NS_FAILED(rv)) {
Fault("unlinking wrong kind of pointer", s);
return NS_ERROR_FAILURE;
}
getCollector()->mStats.mSuccessfulQI++;
rv = cp->Unlink(s);
if (NS_FAILED(rv)) {
Fault("failed unlink", s);
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
nsresult Unroot(const nsDeque &nodes)
{
for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
void *p = nodes.ObjectAt(i);
nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
NS_RELEASE(s);
}
return NS_OK;
}
nsresult FinishCycleCollection()
{
return NS_OK;
}
};
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Extra book-keeping functions. // Extra book-keeping functions.
@ -1218,16 +1201,16 @@ install_new_hooks()
static void* static void*
my_realloc_hook(void *ptr, size_t size, const void *caller) my_realloc_hook(void *ptr, size_t size, const void *caller)
{ {
void *result; void *result;
nsCycleCollector* cc = getCollector();
install_old_hooks(); install_old_hooks();
result = realloc(ptr, size); result = realloc(ptr, size);
save_old_hooks(); save_old_hooks();
cc->Freed(ptr); if (sCollector) {
sCollector->Freed(ptr);
cc->Allocated(result, size); sCollector->Allocated(result, size);
}
install_new_hooks(); install_new_hooks();
@ -1244,7 +1227,8 @@ my_memalign_hook(size_t size, size_t alignment, const void *caller)
result = memalign(size, alignment); result = memalign(size, alignment);
save_old_hooks(); save_old_hooks();
getCollector()->Allocated(result, size); if (sCollector)
sCollector->Allocated(result, size);
install_new_hooks(); install_new_hooks();
@ -1259,7 +1243,8 @@ my_free_hook (void *ptr, const void *caller)
free(ptr); free(ptr);
save_old_hooks(); save_old_hooks();
getCollector()->Freed(ptr); if (sCollector)
sCollector->Freed(ptr);
install_new_hooks(); install_new_hooks();
} }
@ -1268,13 +1253,14 @@ my_free_hook (void *ptr, const void *caller)
static void* static void*
my_malloc_hook (size_t size, const void *caller) my_malloc_hook (size_t size, const void *caller)
{ {
void *result; void *result;
install_old_hooks(); install_old_hooks();
result = malloc (size); result = malloc (size);
save_old_hooks(); save_old_hooks();
getCollector()->Allocated(result, size); if (sCollector)
sCollector->Allocated(result, size);
install_new_hooks(); install_new_hooks();
@ -1301,7 +1287,7 @@ AllocHook(int allocType, void *userData, size_t size, int
lineNumber) lineNumber)
{ {
if (allocType == _HOOK_FREE) if (allocType == _HOOK_FREE)
getCollector()->Freed(userData); sCollector->Freed(userData);
return 1; return 1;
} }
@ -1324,7 +1310,8 @@ static void (*old_free)(struct _malloc_zone_t *zone, void *ptr);
static void static void
freehook(struct _malloc_zone_t *zone, void *ptr) freehook(struct _malloc_zone_t *zone, void *ptr)
{ {
getCollector()->Freed(ptr); if (sCollector)
sCollector->Freed(ptr);
old_free(zone, ptr); old_free(zone, ptr);
} }
@ -1356,8 +1343,6 @@ InitMemHook(void)
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
nsCycleCollector::nsCycleCollector() : nsCycleCollector::nsCycleCollector() :
mTick(0),
mLastAging(0),
mCollectionInProgress(PR_FALSE), mCollectionInProgress(PR_FALSE),
mScanInProgress(PR_FALSE), mScanInProgress(PR_FALSE),
mPurpleBuf(mParams, mStats), mPurpleBuf(mParams, mStats),
@ -1368,31 +1353,15 @@ nsCycleCollector::nsCycleCollector() :
mExpectedGarbage.Init(); mExpectedGarbage.Init();
#endif #endif
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) { memset(mRuntimes, 0, sizeof(mRuntimes));
mRuntimes[i] = nsnull; mRuntimes[nsIProgrammingLanguage::CPLUSPLUS] = &mXPCOMRuntime;
mBufs[i] = new nsDeque(nsnull);
}
mRuntimes[nsIProgrammingLanguage::CPLUSPLUS]
= new nsCycleCollectionXPCOMRuntime();
} }
nsCycleCollector::~nsCycleCollector() nsCycleCollector::~nsCycleCollector()
{ {
if (this == sCollector)
sCollector = nsnull;
mGraph.Clear(); mGraph.Clear();
for (PRUint32 i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
delete mBufs[i];
mBufs[i] = NULL;
}
delete mRuntimes[nsIProgrammingLanguage::CPLUSPLUS];
mRuntimes[nsIProgrammingLanguage::CPLUSPLUS] = NULL;
for (PRUint32 i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) { for (PRUint32 i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
mRuntimes[i] = NULL; mRuntimes[i] = NULL;
} }
@ -1440,11 +1409,11 @@ nsCycleCollector::MaybeDrawGraphs()
PRUint32 i; PRUint32 i;
nsDeque roots(nsnull); nsDeque roots(nsnull);
while (mBufs[0]->GetSize() > 0) while (mBufs[0].GetSize() > 0)
roots.Push(mBufs[0]->Pop()); roots.Push(mBufs[0].Pop());
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
mBufs[i]->Empty(); mBufs[i].Empty();
mGraph.Enumerate(FindWhiteCallback, this); mGraph.Enumerate(FindWhiteCallback, this);
@ -1452,7 +1421,7 @@ nsCycleCollector::MaybeDrawGraphs()
PRBool anyWhites = PR_FALSE; PRBool anyWhites = PR_FALSE;
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) { for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
if (mBufs[i]->GetSize() > 0) { if (mBufs[i].GetSize() > 0) {
anyWhites = PR_TRUE; anyWhites = PR_TRUE;
break; break;
} }
@ -1468,7 +1437,7 @@ nsCycleCollector::MaybeDrawGraphs()
} }
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
mBufs[i]->Empty(); mBufs[i].Empty();
} }
} }
@ -1553,7 +1522,7 @@ nsCycleCollector::Suspect(nsISupports *n, PRBool current)
#endif #endif
if (current) if (current)
mBufs[0]->Push(n); mBufs[0].Push(n);
else else
mPurpleBuf.Put(n); mPurpleBuf.Put(n);
@ -1646,10 +1615,10 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
// GC calls -- so it's essential that we actually execute this // GC calls -- so it's essential that we actually execute this
// step! // step!
// //
// It is also essential to empty mBufs->[0] here because starting up // It is also essential to empty mBufs[0] here because starting up
// collection in language runtimes may force some "current" suspects // collection in language runtimes may force some "current" suspects
// into mBufs[0]. // into mBufs[0].
mBufs[0]->Empty(); mBufs[0].Empty();
#ifdef COLLECT_TIME_DEBUG #ifdef COLLECT_TIME_DEBUG
now = PR_Now(); now = PR_Now();
@ -1678,7 +1647,7 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
(PR_Now() - now) / PR_USEC_PER_MSEC); (PR_Now() - now) / PR_USEC_PER_MSEC);
#endif #endif
if (mBufs[0]->GetSize() == 0) { if (mBufs[0].GetSize() == 0) {
aTryCollections = 0; aTryCollections = 0;
} else { } else {
if (mCollectionInProgress) if (mCollectionInProgress)
@ -1744,7 +1713,7 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) { for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i]) if (mRuntimes[i])
mRuntimes[i]->FinishCycleCollection(); mRuntimes[i]->FinishCycleCollection();
} }
} }
@ -1767,7 +1736,7 @@ nsCycleCollector::Shutdown()
#ifdef DEBUG #ifdef DEBUG
CollectPurple(); CollectPurple();
if (mBufs[0]->GetSize() != 0) { if (mBufs[0].GetSize() != 0) {
printf("Might have been able to release more cycles if the cycle collector would " printf("Might have been able to release more cycles if the cycle collector would "
"run once more at shutdown.\n"); "run once more at shutdown.\n");
} }
@ -1783,7 +1752,7 @@ PR_STATIC_CALLBACK(PLDHashOperator)
AddExpectedGarbage(nsClearingVoidPtrHashKey *p, void *arg) AddExpectedGarbage(nsClearingVoidPtrHashKey *p, void *arg)
{ {
nsCycleCollector *c = NS_STATIC_CAST(nsCycleCollector*, arg); nsCycleCollector *c = NS_STATIC_CAST(nsCycleCollector*, arg);
c->mBufs[0]->Push(NS_CONST_CAST(void*, p->GetKey())); c->mBufs[0].Push(NS_CONST_CAST(void*, p->GetKey()));
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
@ -1849,7 +1818,7 @@ nsCycleCollector::ExplainLiveExpectedGarbage()
mScanInProgress = PR_TRUE; mScanInProgress = PR_TRUE;
mGraph.Clear(); mGraph.Clear();
mBufs[0]->Empty(); mBufs[0].Empty();
// Instead of filling mBufs[0] from the purple buffer, we fill it // Instead of filling mBufs[0] from the purple buffer, we fill it
// from the list of nodes we were expected to collect. // from the list of nodes we were expected to collect.
@ -1860,8 +1829,8 @@ nsCycleCollector::ExplainLiveExpectedGarbage()
mScanInProgress = PR_FALSE; mScanInProgress = PR_FALSE;
for (int i = 0; i < mBufs[0]->GetSize(); ++i) { for (int i = 0; i < mBufs[0].GetSize(); ++i) {
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->ObjectAt(i)); nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
s = canonicalize(s); s = canonicalize(s);
explainWalker(mGraph, mRuntimes).Walk(s); explainWalker(mGraph, mRuntimes).Walk(s);
} }
@ -1898,77 +1867,107 @@ void
nsCycleCollector_registerRuntime(PRUint32 langID, nsCycleCollector_registerRuntime(PRUint32 langID,
nsCycleCollectionLanguageRuntime *rt) nsCycleCollectionLanguageRuntime *rt)
{ {
getCollector()->RegisterRuntime(langID, rt); if (sCollector)
sCollector->RegisterRuntime(langID, rt);
} }
void void
nsCycleCollector_forgetRuntime(PRUint32 langID) nsCycleCollector_forgetRuntime(PRUint32 langID)
{ {
getCollector()->ForgetRuntime(langID); if (sCollector)
sCollector->ForgetRuntime(langID);
} }
void void
nsCycleCollector_suspect(nsISupports *n) nsCycleCollector_suspect(nsISupports *n)
{ {
getCollector()->Suspect(n); if (sCollector)
sCollector->Suspect(n);
} }
void void
nsCycleCollector_suspectCurrent(nsISupports *n) nsCycleCollector_suspectCurrent(nsISupports *n)
{ {
getCollector()->Suspect(n, true); if (sCollector)
sCollector->Suspect(n, PR_TRUE);
} }
void void
nsCycleCollector_forget(nsISupports *n) nsCycleCollector_forget(nsISupports *n)
{ {
getCollector()->Forget(n); if (sCollector)
sCollector->Forget(n);
} }
void void
nsCycleCollector_collect() nsCycleCollector_collect()
{ {
getCollector()->Collect(); if (sCollector)
sCollector->Collect();
}
nsresult
nsCycleCollector_startup()
{
NS_ASSERTION(!sCollector, "Forgot to call nsCycleCollector_shutdown?");
sCollector = new nsCycleCollector();
return sCollector ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
} }
void void
nsCycleCollector_shutdown() nsCycleCollector_shutdown()
{ {
getCollector()->Shutdown(); if (sCollector) {
sCollector->Shutdown();
delete sCollector;
sCollector = nsnull;
}
} }
#ifdef DEBUG #ifdef DEBUG
void void
nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n) nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n)
{ {
getCollector()->ShouldBeFreed(n); if (sCollector)
sCollector->ShouldBeFreed(n);
} }
void void
nsCycleCollector_DEBUG_wasFreed(nsISupports *n) nsCycleCollector_DEBUG_wasFreed(nsISupports *n)
{ {
getCollector()->WasFreed(n); if (sCollector)
sCollector->WasFreed(n);
} }
#endif #endif
PRBool PRBool
nsCycleCollector_isScanSafe(nsISupports *s) nsCycleCollector_isScanSafe(nsISupports *s)
{ {
nsresult rv;
if (!s) if (!s)
return PR_FALSE; return PR_FALSE;
nsCOMPtr<nsCycleCollectionParticipant> cp = do_QueryInterface(s, &rv); nsCycleCollectionParticipant *cp;
if (NS_FAILED(rv)) { ToParticipant(s, &cp);
getCollector()->mStats.mFailedQI++;
return PR_FALSE;
}
return PR_TRUE; return cp != nsnull;
}
static void
ToParticipant(nsISupports *s, nsCycleCollectionParticipant **cp)
{
// We use QI to move from an nsISupports to an
// nsCycleCollectionParticipant, which is a per-class singleton helper
// object that implements traversal and unlinking logic for the nsISupports
// in question.
CallQueryInterface(s, cp);
if (cp)
++sCollector->mStats.mSuccessfulQI;
else
++sCollector->mStats.mFailedQI;
} }

View File

@ -63,8 +63,9 @@ NS_COM PRBool nsCycleCollector_isScanSafe(nsISupports *n);
NS_COM void nsCycleCollector_suspect(nsISupports *n); NS_COM void nsCycleCollector_suspect(nsISupports *n);
NS_COM void nsCycleCollector_suspectCurrent(nsISupports *n); NS_COM void nsCycleCollector_suspectCurrent(nsISupports *n);
NS_COM void nsCycleCollector_forget(nsISupports *n); NS_COM void nsCycleCollector_forget(nsISupports *n);
nsresult nsCycleCollector_startup();
NS_COM void nsCycleCollector_collect(); NS_COM void nsCycleCollector_collect();
NS_COM void nsCycleCollector_shutdown(); void nsCycleCollector_shutdown();
#ifdef DEBUG #ifdef DEBUG
NS_COM void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n); NS_COM void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n);

View File

@ -592,10 +592,13 @@ NS_InitXPCOM3(nsIServiceManager* *result,
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
#ifdef GC_LEAK_DETECTOR #ifdef GC_LEAK_DETECTOR
rv = NS_InitLeakDetector(); rv = NS_InitLeakDetector();
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
#endif #endif
rv = nsCycleCollector_startup();
if (NS_FAILED(rv)) return rv;
// 2. Register the global services with the component manager so that // 2. Register the global services with the component manager so that
// clients can create new objects. // clients can create new objects.

View File

@ -89,7 +89,7 @@ class nsDequeIterator;
class NS_COM nsDeque { class NS_COM nsDeque {
friend class nsDequeIterator; friend class nsDequeIterator;
public: public:
nsDeque(nsDequeFunctor* aDeallocator); nsDeque(nsDequeFunctor* aDeallocator = nsnull);
~nsDeque(); ~nsDeque();
/** /**
@ -218,11 +218,6 @@ protected:
private: private:
/**
* Simple default constructor (PRIVATE)
*/
nsDeque();
/** /**
* Copy constructor (PRIVATE) * Copy constructor (PRIVATE)
* *