Fix for bug 374096 (Cycle collector doesn't collect all the cycles it could). r=graydon, sr=dbaron.

This commit is contained in:
peterv%propagandism.org 2007-03-16 12:52:47 +00:00
Родитель 075d3d5566
Коммит 5a1ed9c668
5 изменённых файлов: 131 добавлений и 97 удалений

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

@ -2266,6 +2266,7 @@ public:
NS_DECL_NSIPROPERTYBAG
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS,
nsIXPConnectWrappedJS)
NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(nsXPCWrappedJS)
NS_IMETHOD CallMethod(PRUint16 methodIndex,
const XPTMethodDescriptor *info,

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

@ -66,6 +66,10 @@ nsCycleCollectionParticipant::Traverse(nsISupports *n,
return NS_OK;
}
NS_IMETHODIMP_(void) nsCycleCollectionParticipant::UnmarkPurple(nsISupports *n)
{
}
#ifdef DEBUG
PRBool
nsCycleCollectionParticipant::CheckForRightISupports(nsISupports *s)

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

@ -41,11 +41,11 @@
#include "nsISupports.h"
#define NS_CYCLECOLLECTIONPARTICIPANT_IID \
{ /* 407bbd61-13b2-4f85-bdc6-23a59d881f80 */ \
0x407bbd61, \
0x13b2, \
0x4f85, \
{ 0xbd, 0xc6, 0x23, 0xa5, 0x9d, 0x88, 0x1f, 0x80 } \
{ \
0x8057ad9e, \
0x3ecc, \
0x4e89, \
{ 0x89, 0xac, 0x0a, 0x71, 0xf3, 0x8b, 0x14, 0xc2 } \
}
/**
@ -100,6 +100,7 @@ public:
NS_IMETHOD Unlink(nsISupports *p);
NS_IMETHOD Traverse(nsISupports *p,
nsCycleCollectionTraversalCallback &cb);
NS_IMETHOD_(void) UnmarkPurple(nsISupports *p);
#ifdef DEBUG
NS_EXTERNAL_VIS_(PRBool) CheckForRightISupports(nsISupports *s);
@ -249,6 +250,10 @@ public: \
NS_IMETHOD Unlink(nsISupports *n); \
NS_IMETHOD Traverse(nsISupports *n, \
nsCycleCollectionTraversalCallback &cb); \
NS_IMETHOD_(void) UnmarkPurple(nsISupports *n) \
{ \
Downcast(n)->UnmarkPurple(); \
} \
static _class* Downcast(nsISupports* s) \
{ \
return NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base*, s)); \
@ -292,6 +297,17 @@ public: \
} \
};
/**
* This implements a stub UnmarkPurple function for classes that want to be
* traversed but whose AddRef/Release functions don't add/remove them to/from
* the purple buffer. If you're just using NS_DECL_CYCLE_COLLECTING_ISUPPORTS
* then you don't need this.
*/
#define NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(_class) \
void UnmarkPurple() \
{ \
} \
#define NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
static NS_CYCLE_COLLECTION_CLASSNAME(_class) \
NS_CYCLE_COLLECTION_NAME(_class);

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

@ -156,6 +156,7 @@ struct nsCycleCollectorParams
PRBool mLogPointers;
PRUint32 mScanDelay;
PRUint32 mShutdownCollections;
nsCycleCollectorParams() :
mDoNothing (PR_GetEnv("XPCOM_CC_DO_NOTHING") != NULL),
@ -174,11 +175,15 @@ struct nsCycleCollectorParams
// - More time to be spent in the collector (bad)
// - Less delay between forming garbage and collecting it (good)
mScanDelay(10)
mScanDelay(10),
mShutdownCollections(5)
{
char *s = PR_GetEnv("XPCOM_CC_SCAN_DELAY");
if (s)
PR_sscanf(s, "%d", &mScanDelay);
s = PR_GetEnv("XPCOM_CC_SHUTDOWN_COLLECTIONS");
if (s)
PR_sscanf(s, "%d", &mShutdownCollections);
}
};
@ -488,7 +493,6 @@ struct nsCycleCollector
void MarkRoots();
void ScanRoots();
void CollectWhite();
void ForgetAll();
nsCycleCollector();
~nsCycleCollector();
@ -497,7 +501,7 @@ struct nsCycleCollector
void Forget(nsISupports *n);
void Allocated(void *n, size_t sz);
void Freed(void *n);
void Collect();
void Collect(PRUint32 aTryCollections = 1);
void Shutdown();
#ifdef DEBUG
@ -884,13 +888,28 @@ FindWhiteCallback(const void* ptr,
{
nsCycleCollector *collector = NS_STATIC_CAST(nsCycleCollector*,
userArg);
void* p = NS_CONST_CAST(void*, ptr);
NS_ASSERTION(pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS ||
!collector->mPurpleBuf.Exists(p),
"Need to remove non-CPLUSPLUS objects from purple buffer!");
if (pinfo.mColor == white) {
nsISupports *s = NS_STATIC_CAST(nsISupports *,
NS_CONST_CAST(void*, ptr));
if (pinfo.mLang > nsIProgrammingLanguage::MAX)
Fault("White node has bad language ID", s);
Fault("White node has bad language ID", p);
else
collector->mBufs[pinfo.mLang]->Push(s);
collector->mBufs[pinfo.mLang]->Push(p);
if (pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS) {
nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
collector->Forget(s);
}
}
else if (pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS) {
nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
nsCycleCollectionParticipant* cp;
CallQueryInterface(s, &cp);
if (cp)
cp->UnmarkPurple(s);
collector->Forget(s);
}
return PL_DHASH_NEXT;
}
@ -1069,34 +1088,6 @@ struct nsCycleCollectionXPCOMRuntime :
// Extra book-keeping functions.
////////////////////////////////////////////////////////////////////////
static PR_CALLBACK PLDHashOperator
ForgetAllCallback(const void* ptr,
PtrInfo& pinfo,
void* userArg)
{
nsCycleCollector *collector = NS_STATIC_CAST(nsCycleCollector*,
userArg);
nsISupports *root = NS_STATIC_CAST(nsISupports *,
NS_CONST_CAST(void*, ptr));
collector->mBufs[0]->Push(root);
return PL_DHASH_NEXT;
}
void
nsCycleCollector::ForgetAll()
{
mBufs[0]->Empty();
mGraph.Enumerate(ForgetAllCallback, this);
while (mBufs[0]->GetSize() > 0) {
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->Pop());
Forget(s);
}
mBufs[0]->Empty();
}
struct graphVizWalker : public GraphWalker
{
// We can't just use _popen here because graphviz-for-windows
@ -1637,73 +1628,76 @@ nsCycleCollector::Freed(void *n)
void
nsCycleCollector::Collect()
nsCycleCollector::Collect(PRUint32 aTryCollections)
{
// This triggers a JS GC. Our caller assumes we always trigger at
// least one JS GC -- they rely on this fact to avoid redundant JS
// GC calls -- so it's essential that we actually execute this
// step!
//
// It is also essential to empty mBufs->[0] here because starting up
// collection in language runtimes may force some "current" suspects
// into mBufs[0].
mBufs[0]->Empty();
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->BeginCycleCollection();
}
if (! mParams.mDoNothing) {
#ifndef __MINGW32__
if (mParams.mHookMalloc)
InitMemHook();
if (!mParams.mDoNothing && mParams.mHookMalloc)
InitMemHook();
#endif
CollectPurple();
if (mBufs[0]->GetSize() == 0) {
mPurpleBuf.BumpGeneration();
mStats.mCollection++;
if (mParams.mReportStats)
mStats.Dump();
while (aTryCollections > 0) {
// This triggers a JS GC. Our caller assumes we always trigger at
// least one JS GC -- they rely on this fact to avoid redundant JS
// GC calls -- so it's essential that we actually execute this
// step!
//
// It is also essential to empty mBufs->[0] here because starting up
// collection in language runtimes may force some "current" suspects
// into mBufs[0].
mBufs[0]->Empty();
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->BeginCycleCollection();
}
if (mParams.mDoNothing) {
aTryCollections = 0;
} else {
if (mCollectionInProgress)
Fault("re-entered collection");
mCollectionInProgress = PR_TRUE;
mScanInProgress = PR_TRUE;
mGraph.Clear();
// The main Bacon & Rajan collection algorithm.
MarkRoots();
ScanRoots();
mScanInProgress = PR_FALSE;
MaybeDrawGraphs();
CollectWhite();
ForgetAll();
// Some additional book-keeping.
mGraph.Clear();
CollectPurple();
if (mBufs[0]->GetSize() == 0) {
aTryCollections = 0;
} else {
if (mCollectionInProgress)
Fault("re-entered collection");
mCollectionInProgress = PR_TRUE;
mScanInProgress = PR_TRUE;
mGraph.Clear();
// The main Bacon & Rajan collection algorithm.
MarkRoots();
ScanRoots();
MaybeDrawGraphs();
mScanInProgress = PR_FALSE;
CollectWhite();
// Some additional book-keeping.
mGraph.Clear();
--aTryCollections;
}
mPurpleBuf.BumpGeneration();
mStats.mCollection++;
if (mParams.mReportStats)
mStats.Dump();
mCollectionInProgress = PR_FALSE;
}
}
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
if (mRuntimes[i])
mRuntimes[i]->FinishCycleCollection();
}
}
}
}
void
@ -1715,8 +1709,15 @@ nsCycleCollector::Shutdown()
mPurpleBuf.BumpGeneration();
mParams.mScanDelay = 0;
Collect();
Collect(mParams.mShutdownCollections);
#ifdef DEBUG
CollectPurple();
if (mBufs[0]->GetSize() != 0) {
printf("Might have been able to release more cycles if the cycle collector would "
"run once more at shutdown.\n");
}
ExplainLiveExpectedGarbage();
#endif
mParams.mDoNothing = PR_TRUE;

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

@ -171,6 +171,12 @@ public:
return get();
}
void unmarkPurple()
{
if (NS_LIKELY(mValue != NS_PURPLE_BIT))
NS_CLEAR_PURPLE_BIT(mValue);
}
nsrefcnt get() const
{
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
@ -232,6 +238,10 @@ public: \
void** aInstancePtr); \
NS_IMETHOD_(nsrefcnt) AddRef(void); \
NS_IMETHOD_(nsrefcnt) Release(void); \
void UnmarkPurple() \
{ \
mRefCnt.unmarkPurple(); \
} \
protected: \
nsCycleCollectingAutoRefCnt mRefCnt; \
NS_DECL_OWNINGTHREAD \
@ -345,7 +355,8 @@ NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void) \
{ \
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
nsrefcnt count = mRefCnt.incr(NS_STATIC_CAST(_basetype *, this)); \
nsrefcnt count = \
mRefCnt.incr(NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this)); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
return count; \
}
@ -358,10 +369,11 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
{ \
NS_PRECONDITION(0 != mRefCnt, "dup release"); \
NS_ASSERT_OWNINGTHREAD(_class); \
nsrefcnt count = mRefCnt.decr(NS_STATIC_CAST(_basetype *, this)); \
nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
nsrefcnt count = mRefCnt.decr(base); \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
mRefCnt.stabilizeForDeletion(NS_STATIC_CAST(_basetype *, this)); \
mRefCnt.stabilizeForDeletion(base); \
_destroy; \
return 0; \
} \