diff --git a/mozilla/js/src/xpconnect/src/xpcprivate.h b/mozilla/js/src/xpconnect/src/xpcprivate.h index f799c063c37..54496a9d7b5 100644 --- a/mozilla/js/src/xpconnect/src/xpcprivate.h +++ b/mozilla/js/src/xpconnect/src/xpcprivate.h @@ -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, diff --git a/mozilla/xpcom/base/nsCycleCollectionParticipant.cpp b/mozilla/xpcom/base/nsCycleCollectionParticipant.cpp index 6ded15b94e4..30c6e00047b 100644 --- a/mozilla/xpcom/base/nsCycleCollectionParticipant.cpp +++ b/mozilla/xpcom/base/nsCycleCollectionParticipant.cpp @@ -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) diff --git a/mozilla/xpcom/base/nsCycleCollectionParticipant.h b/mozilla/xpcom/base/nsCycleCollectionParticipant.h index ebf8b05ed9e..39d7f30553b 100644 --- a/mozilla/xpcom/base/nsCycleCollectionParticipant.h +++ b/mozilla/xpcom/base/nsCycleCollectionParticipant.h @@ -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); diff --git a/mozilla/xpcom/base/nsCycleCollector.cpp b/mozilla/xpcom/base/nsCycleCollector.cpp index bdf2894d884..3c4d6d1d012 100644 --- a/mozilla/xpcom/base/nsCycleCollector.cpp +++ b/mozilla/xpcom/base/nsCycleCollector.cpp @@ -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; diff --git a/mozilla/xpcom/glue/nsISupportsImpl.h b/mozilla/xpcom/glue/nsISupportsImpl.h index f983d1b3e25..4e6bcffbc95 100644 --- a/mozilla/xpcom/glue/nsISupportsImpl.h +++ b/mozilla/xpcom/glue/nsISupportsImpl.h @@ -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; \ } \