зеркало из https://github.com/mozilla/pjs.git
Fix for bug 374096 (Cycle collector doesn't collect all the cycles it could). r=graydon, sr=dbaron.
This commit is contained in:
Родитель
c51ca749ed
Коммит
09b00a6b2d
|
@ -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; \
|
||||
} \
|
||||
|
|
Загрузка…
Ссылка в новой задаче