зеркало из https://github.com/mozilla/pjs.git
Fix for bug 375075 (Stop leaking the cycle collector on shutdown). r=bsmedberg, sr=dbaron.
This commit is contained in:
Родитель
c56257f3ec
Коммит
84e7da2624
|
@ -135,6 +135,7 @@
|
|||
#include "plstr.h"
|
||||
#include "prtime.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
|
@ -256,6 +257,9 @@ struct nsCycleCollectorStats
|
|||
}
|
||||
};
|
||||
|
||||
static void
|
||||
ToParticipant(nsISupports *s, nsCycleCollectionParticipant **cp);
|
||||
|
||||
static PRBool
|
||||
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
|
||||
{
|
||||
PRUint32 mTick;
|
||||
PRTime mLastAging;
|
||||
PRBool mCollectionInProgress;
|
||||
PRBool mScanInProgress;
|
||||
|
||||
GCTable mGraph;
|
||||
nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
|
||||
nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
|
||||
|
||||
// The set of buffers |mBufs| serves a variety of purposes; mostly
|
||||
// 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
|
||||
// per-language (as a set-of-pointers-in-language-N).
|
||||
|
||||
nsDeque *mBufs[nsIProgrammingLanguage::MAX+1];
|
||||
nsDeque mBufs[nsIProgrammingLanguage::MAX + 1];
|
||||
|
||||
nsCycleCollectorParams mParams;
|
||||
nsCycleCollectorStats mStats;
|
||||
|
@ -563,17 +651,6 @@ static nsCycleCollector *sCollector = nsnull;
|
|||
// Utility functions
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static inline nsCycleCollector*
|
||||
getCollector()
|
||||
{
|
||||
if (!sCollector)
|
||||
sCollector = new nsCycleCollector;
|
||||
|
||||
return sCollector;
|
||||
}
|
||||
|
||||
|
||||
struct safetyCallback :
|
||||
public nsCycleCollectionTraversalCallback
|
||||
{
|
||||
|
@ -602,9 +679,11 @@ EnsurePtrInfo(GCTable & tab, void *n, PtrInfo & pi)
|
|||
static void
|
||||
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)
|
||||
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
|
||||
// *should* never hit a fault.
|
||||
|
||||
cc->mParams.mDoNothing = PR_TRUE;
|
||||
sCollector->mParams.mDoNothing = PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GraphWalker::DescribeNode(size_t refCount, size_t objSz, const char *objName)
|
||||
{
|
||||
nsCycleCollector* cc = getCollector();
|
||||
|
||||
if (refCount == 0)
|
||||
Fault("zero refcount", mCurrPtr);
|
||||
|
||||
mCurrPi.mBytes = objSz;
|
||||
mCurrPi.mName = objName;
|
||||
this->VisitNode(mCurrPtr, mCurrPi, refCount);
|
||||
cc->mStats.mVisitedNode++;
|
||||
sCollector->mStats.mVisitedNode++;
|
||||
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.mRefCount = refcount;
|
||||
getCollector()->mStats.mSetColorGrey++;
|
||||
sCollector->mStats.mSetColorGrey++;
|
||||
mGraph.Put(p, pi);
|
||||
}
|
||||
|
||||
|
@ -767,16 +844,16 @@ struct MarkGreyWalker : public GraphWalker
|
|||
void
|
||||
nsCycleCollector::CollectPurple()
|
||||
{
|
||||
mPurpleBuf.SelectAgedPointers(mBufs[0]);
|
||||
mPurpleBuf.SelectAgedPointers(&mBufs[0]);
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector::MarkRoots()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < mBufs[0]->GetSize(); ++i) {
|
||||
for (i = 0; i < mBufs[0].GetSize(); ++i) {
|
||||
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);
|
||||
EnsurePtrInfo(mGraph, s, pi);
|
||||
MarkGreyWalker(mGraph, mRuntimes).Walk(s);
|
||||
|
@ -804,7 +881,7 @@ struct ScanBlackWalker : public GraphWalker
|
|||
void VisitNode(void *p, PtrInfo & pi, size_t refcount)
|
||||
{
|
||||
pi.mColor = black;
|
||||
getCollector()->mStats.mSetColorBlack++;
|
||||
sCollector->mStats.mSetColorBlack++;
|
||||
mGraph.Put(p, pi);
|
||||
}
|
||||
|
||||
|
@ -826,8 +903,6 @@ struct scanWalker : public GraphWalker
|
|||
|
||||
void VisitNode(void *p, PtrInfo & pi, size_t refcount)
|
||||
{
|
||||
nsCycleCollector* cc = getCollector();
|
||||
|
||||
if (pi.mColor != grey)
|
||||
Fault("scanning non-grey node", p);
|
||||
|
||||
|
@ -836,11 +911,11 @@ struct scanWalker : public GraphWalker
|
|||
|
||||
if (pi.mInternalRefs == refcount) {
|
||||
pi.mColor = white;
|
||||
cc->mStats.mSetColorWhite++;
|
||||
sCollector->mStats.mSetColorWhite++;
|
||||
} else {
|
||||
ScanBlackWalker(mGraph, mRuntimes).Walk(p);
|
||||
pi.mColor = black;
|
||||
cc->mStats.mSetColorBlack++;
|
||||
sCollector->mStats.mSetColorBlack++;
|
||||
}
|
||||
mGraph.Put(p, pi);
|
||||
}
|
||||
|
@ -864,8 +939,8 @@ nsCycleCollector::ScanRoots()
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mBufs[0]->GetSize(); ++i) {
|
||||
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->ObjectAt(i));
|
||||
for (i = 0; i < mBufs[0].GetSize(); ++i) {
|
||||
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
|
||||
s = canonicalize(s);
|
||||
scanWalker(mGraph, mRuntimes).Walk(s);
|
||||
}
|
||||
|
@ -896,7 +971,7 @@ FindWhiteCallback(const void* ptr,
|
|||
if (pinfo.mLang > nsIProgrammingLanguage::MAX)
|
||||
Fault("White node has bad language ID", p);
|
||||
else
|
||||
collector->mBufs[pinfo.mLang]->Push(p);
|
||||
collector->mBufs[pinfo.mLang].Push(p);
|
||||
|
||||
if (pinfo.mLang == nsIProgrammingLanguage::CPLUSPLUS) {
|
||||
nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
|
||||
|
@ -936,7 +1011,7 @@ nsCycleCollector::CollectWhite()
|
|||
nsresult rv;
|
||||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
|
||||
mBufs[i]->Empty();
|
||||
mBufs[i].Empty();
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#ifdef WIN32
|
||||
|
@ -949,8 +1024,8 @@ nsCycleCollector::CollectWhite()
|
|||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
|
||||
if (mRuntimes[i] &&
|
||||
mBufs[i]->GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Root(*mBufs[i]);
|
||||
mBufs[i].GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Root(mBufs[i]);
|
||||
if (NS_FAILED(rv))
|
||||
Fault("Failed root call while unlinking");
|
||||
}
|
||||
|
@ -958,28 +1033,28 @@ nsCycleCollector::CollectWhite()
|
|||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
|
||||
if (mRuntimes[i] &&
|
||||
mBufs[i]->GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Unlink(*mBufs[i]);
|
||||
mBufs[i].GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Unlink(mBufs[i]);
|
||||
if (NS_FAILED(rv)) {
|
||||
Fault("Failed unlink call while unlinking");
|
||||
mStats.mFailedUnlink++;
|
||||
} else {
|
||||
mStats.mCollectedNode += mBufs[i]->GetSize();
|
||||
mStats.mCollectedNode += mBufs[i].GetSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
|
||||
if (mRuntimes[i] &&
|
||||
mBufs[i]->GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Unroot(*mBufs[i]);
|
||||
mBufs[i].GetSize() > 0) {
|
||||
rv = mRuntimes[i]->Unroot(mBufs[i]);
|
||||
if (NS_FAILED(rv))
|
||||
Fault("Failed unroot call while unlinking");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
|
||||
mBufs[i]->Empty();
|
||||
mBufs[i].Empty();
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#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.
|
||||
|
@ -1218,16 +1201,16 @@ install_new_hooks()
|
|||
static void*
|
||||
my_realloc_hook(void *ptr, size_t size, const void *caller)
|
||||
{
|
||||
void *result;
|
||||
nsCycleCollector* cc = getCollector();
|
||||
void *result;
|
||||
|
||||
install_old_hooks();
|
||||
result = realloc(ptr, size);
|
||||
save_old_hooks();
|
||||
|
||||
cc->Freed(ptr);
|
||||
|
||||
cc->Allocated(result, size);
|
||||
if (sCollector) {
|
||||
sCollector->Freed(ptr);
|
||||
sCollector->Allocated(result, size);
|
||||
}
|
||||
|
||||
install_new_hooks();
|
||||
|
||||
|
@ -1244,7 +1227,8 @@ my_memalign_hook(size_t size, size_t alignment, const void *caller)
|
|||
result = memalign(size, alignment);
|
||||
save_old_hooks();
|
||||
|
||||
getCollector()->Allocated(result, size);
|
||||
if (sCollector)
|
||||
sCollector->Allocated(result, size);
|
||||
|
||||
install_new_hooks();
|
||||
|
||||
|
@ -1259,7 +1243,8 @@ my_free_hook (void *ptr, const void *caller)
|
|||
free(ptr);
|
||||
save_old_hooks();
|
||||
|
||||
getCollector()->Freed(ptr);
|
||||
if (sCollector)
|
||||
sCollector->Freed(ptr);
|
||||
|
||||
install_new_hooks();
|
||||
}
|
||||
|
@ -1268,13 +1253,14 @@ my_free_hook (void *ptr, const void *caller)
|
|||
static void*
|
||||
my_malloc_hook (size_t size, const void *caller)
|
||||
{
|
||||
void *result;
|
||||
void *result;
|
||||
|
||||
install_old_hooks();
|
||||
result = malloc (size);
|
||||
save_old_hooks();
|
||||
|
||||
getCollector()->Allocated(result, size);
|
||||
if (sCollector)
|
||||
sCollector->Allocated(result, size);
|
||||
|
||||
install_new_hooks();
|
||||
|
||||
|
@ -1301,7 +1287,7 @@ AllocHook(int allocType, void *userData, size_t size, int
|
|||
lineNumber)
|
||||
{
|
||||
if (allocType == _HOOK_FREE)
|
||||
getCollector()->Freed(userData);
|
||||
sCollector->Freed(userData);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1324,7 +1310,8 @@ static void (*old_free)(struct _malloc_zone_t *zone, void *ptr);
|
|||
static void
|
||||
freehook(struct _malloc_zone_t *zone, void *ptr)
|
||||
{
|
||||
getCollector()->Freed(ptr);
|
||||
if (sCollector)
|
||||
sCollector->Freed(ptr);
|
||||
old_free(zone, ptr);
|
||||
}
|
||||
|
||||
|
@ -1356,8 +1343,6 @@ InitMemHook(void)
|
|||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsCycleCollector::nsCycleCollector() :
|
||||
mTick(0),
|
||||
mLastAging(0),
|
||||
mCollectionInProgress(PR_FALSE),
|
||||
mScanInProgress(PR_FALSE),
|
||||
mPurpleBuf(mParams, mStats),
|
||||
|
@ -1368,31 +1353,15 @@ nsCycleCollector::nsCycleCollector() :
|
|||
mExpectedGarbage.Init();
|
||||
#endif
|
||||
|
||||
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
|
||||
mRuntimes[i] = nsnull;
|
||||
mBufs[i] = new nsDeque(nsnull);
|
||||
}
|
||||
|
||||
mRuntimes[nsIProgrammingLanguage::CPLUSPLUS]
|
||||
= new nsCycleCollectionXPCOMRuntime();
|
||||
memset(mRuntimes, 0, sizeof(mRuntimes));
|
||||
mRuntimes[nsIProgrammingLanguage::CPLUSPLUS] = &mXPCOMRuntime;
|
||||
}
|
||||
|
||||
|
||||
nsCycleCollector::~nsCycleCollector()
|
||||
{
|
||||
if (this == sCollector)
|
||||
sCollector = nsnull;
|
||||
|
||||
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) {
|
||||
mRuntimes[i] = NULL;
|
||||
}
|
||||
|
@ -1440,11 +1409,11 @@ nsCycleCollector::MaybeDrawGraphs()
|
|||
PRUint32 i;
|
||||
nsDeque roots(nsnull);
|
||||
|
||||
while (mBufs[0]->GetSize() > 0)
|
||||
roots.Push(mBufs[0]->Pop());
|
||||
while (mBufs[0].GetSize() > 0)
|
||||
roots.Push(mBufs[0].Pop());
|
||||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
|
||||
mBufs[i]->Empty();
|
||||
mBufs[i].Empty();
|
||||
|
||||
mGraph.Enumerate(FindWhiteCallback, this);
|
||||
|
||||
|
@ -1452,7 +1421,7 @@ nsCycleCollector::MaybeDrawGraphs()
|
|||
PRBool anyWhites = PR_FALSE;
|
||||
|
||||
for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
|
||||
if (mBufs[i]->GetSize() > 0) {
|
||||
if (mBufs[i].GetSize() > 0) {
|
||||
anyWhites = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
|
@ -1468,7 +1437,7 @@ nsCycleCollector::MaybeDrawGraphs()
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
if (current)
|
||||
mBufs[0]->Push(n);
|
||||
mBufs[0].Push(n);
|
||||
else
|
||||
mPurpleBuf.Put(n);
|
||||
|
||||
|
@ -1646,10 +1615,10 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
|
|||
// GC calls -- so it's essential that we actually execute this
|
||||
// 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
|
||||
// into mBufs[0].
|
||||
mBufs[0]->Empty();
|
||||
mBufs[0].Empty();
|
||||
|
||||
#ifdef COLLECT_TIME_DEBUG
|
||||
now = PR_Now();
|
||||
|
@ -1678,7 +1647,7 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
|
|||
(PR_Now() - now) / PR_USEC_PER_MSEC);
|
||||
#endif
|
||||
|
||||
if (mBufs[0]->GetSize() == 0) {
|
||||
if (mBufs[0].GetSize() == 0) {
|
||||
aTryCollections = 0;
|
||||
} else {
|
||||
if (mCollectionInProgress)
|
||||
|
@ -1744,7 +1713,7 @@ nsCycleCollector::Collect(PRUint32 aTryCollections)
|
|||
|
||||
for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
|
||||
if (mRuntimes[i])
|
||||
mRuntimes[i]->FinishCycleCollection();
|
||||
mRuntimes[i]->FinishCycleCollection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1767,7 +1736,7 @@ nsCycleCollector::Shutdown()
|
|||
|
||||
#ifdef DEBUG
|
||||
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 "
|
||||
"run once more at shutdown.\n");
|
||||
}
|
||||
|
@ -1783,7 +1752,7 @@ PR_STATIC_CALLBACK(PLDHashOperator)
|
|||
AddExpectedGarbage(nsClearingVoidPtrHashKey *p, void *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;
|
||||
}
|
||||
|
||||
|
@ -1849,7 +1818,7 @@ nsCycleCollector::ExplainLiveExpectedGarbage()
|
|||
mScanInProgress = PR_TRUE;
|
||||
|
||||
mGraph.Clear();
|
||||
mBufs[0]->Empty();
|
||||
mBufs[0].Empty();
|
||||
|
||||
// Instead of filling mBufs[0] from the purple buffer, we fill it
|
||||
// from the list of nodes we were expected to collect.
|
||||
|
@ -1860,8 +1829,8 @@ nsCycleCollector::ExplainLiveExpectedGarbage()
|
|||
|
||||
mScanInProgress = PR_FALSE;
|
||||
|
||||
for (int i = 0; i < mBufs[0]->GetSize(); ++i) {
|
||||
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0]->ObjectAt(i));
|
||||
for (int i = 0; i < mBufs[0].GetSize(); ++i) {
|
||||
nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
|
||||
s = canonicalize(s);
|
||||
explainWalker(mGraph, mRuntimes).Walk(s);
|
||||
}
|
||||
|
@ -1898,77 +1867,107 @@ void
|
|||
nsCycleCollector_registerRuntime(PRUint32 langID,
|
||||
nsCycleCollectionLanguageRuntime *rt)
|
||||
{
|
||||
getCollector()->RegisterRuntime(langID, rt);
|
||||
if (sCollector)
|
||||
sCollector->RegisterRuntime(langID, rt);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCycleCollector_forgetRuntime(PRUint32 langID)
|
||||
{
|
||||
getCollector()->ForgetRuntime(langID);
|
||||
if (sCollector)
|
||||
sCollector->ForgetRuntime(langID);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCycleCollector_suspect(nsISupports *n)
|
||||
{
|
||||
getCollector()->Suspect(n);
|
||||
if (sCollector)
|
||||
sCollector->Suspect(n);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCycleCollector_suspectCurrent(nsISupports *n)
|
||||
{
|
||||
getCollector()->Suspect(n, true);
|
||||
if (sCollector)
|
||||
sCollector->Suspect(n, PR_TRUE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCycleCollector_forget(nsISupports *n)
|
||||
{
|
||||
getCollector()->Forget(n);
|
||||
if (sCollector)
|
||||
sCollector->Forget(n);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
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
|
||||
nsCycleCollector_shutdown()
|
||||
{
|
||||
getCollector()->Shutdown();
|
||||
if (sCollector) {
|
||||
sCollector->Shutdown();
|
||||
delete sCollector;
|
||||
sCollector = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n)
|
||||
{
|
||||
getCollector()->ShouldBeFreed(n);
|
||||
if (sCollector)
|
||||
sCollector->ShouldBeFreed(n);
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector_DEBUG_wasFreed(nsISupports *n)
|
||||
{
|
||||
getCollector()->WasFreed(n);
|
||||
if (sCollector)
|
||||
sCollector->WasFreed(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
PRBool
|
||||
nsCycleCollector_isScanSafe(nsISupports *s)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (!s)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsCycleCollectionParticipant> cp = do_QueryInterface(s, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
getCollector()->mStats.mFailedQI++;
|
||||
return PR_FALSE;
|
||||
}
|
||||
nsCycleCollectionParticipant *cp;
|
||||
ToParticipant(s, &cp);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -63,8 +63,9 @@ NS_COM PRBool nsCycleCollector_isScanSafe(nsISupports *n);
|
|||
NS_COM void nsCycleCollector_suspect(nsISupports *n);
|
||||
NS_COM void nsCycleCollector_suspectCurrent(nsISupports *n);
|
||||
NS_COM void nsCycleCollector_forget(nsISupports *n);
|
||||
nsresult nsCycleCollector_startup();
|
||||
NS_COM void nsCycleCollector_collect();
|
||||
NS_COM void nsCycleCollector_shutdown();
|
||||
void nsCycleCollector_shutdown();
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_COM void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n);
|
||||
|
|
|
@ -592,10 +592,13 @@ NS_InitXPCOM3(nsIServiceManager* *result,
|
|||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
#ifdef GC_LEAK_DETECTOR
|
||||
rv = NS_InitLeakDetector();
|
||||
rv = NS_InitLeakDetector();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
#endif
|
||||
|
||||
rv = nsCycleCollector_startup();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// 2. Register the global services with the component manager so that
|
||||
// clients can create new objects.
|
||||
|
||||
|
|
|
@ -1,405 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* MODULE NOTES:
|
||||
*
|
||||
* The Deque is a very small, very efficient container object
|
||||
* than can hold elements of type void*, offering the following features:
|
||||
* Its interface supports pushing and popping of elements.
|
||||
* It can iterate (via an interator class) its elements.
|
||||
* When full, it can efficiently resize dynamically.
|
||||
*
|
||||
*
|
||||
* NOTE: The only bit of trickery here is that this deque is
|
||||
* built upon a ring-buffer. Like all ring buffers, the first
|
||||
* element may not be at index[0]. The mOrigin member determines
|
||||
* where the first child is. This point is quietly hidden from
|
||||
* customers of this class.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NSDEQUE
|
||||
#define _NSDEQUE
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
/**
|
||||
* The nsDequeFunctor class is used when you want to create
|
||||
* callbacks between the deque and your generic code.
|
||||
* Use these objects in a call to ForEach();
|
||||
*
|
||||
*/
|
||||
|
||||
class nsDequeFunctor{
|
||||
public:
|
||||
virtual void* operator()(void* anObject)=0;
|
||||
};
|
||||
|
||||
/******************************************************
|
||||
* Here comes the nsDeque class itself...
|
||||
******************************************************/
|
||||
|
||||
/**
|
||||
* The deque (double-ended queue) class is a common container type,
|
||||
* whose behavior mimics a line in your favorite checkout stand.
|
||||
* Classic CS describes the common behavior of a queue as FIFO.
|
||||
* A deque allows insertion and removal at both ends of
|
||||
* the container.
|
||||
*
|
||||
* The deque stores pointers to items.
|
||||
*/
|
||||
|
||||
class nsDequeIterator;
|
||||
|
||||
class NS_COM nsDeque {
|
||||
friend class nsDequeIterator;
|
||||
public:
|
||||
nsDeque(nsDequeFunctor* aDeallocator);
|
||||
~nsDeque();
|
||||
|
||||
/**
|
||||
* Returns the number of elements currently stored in
|
||||
* this deque.
|
||||
*
|
||||
* @return number of elements currently in the deque
|
||||
*/
|
||||
inline PRInt32 GetSize() const {return mSize;}
|
||||
|
||||
/**
|
||||
* Appends new member at the end of the deque.
|
||||
*
|
||||
* @param item to store in deque
|
||||
* @return *this
|
||||
*/
|
||||
nsDeque& Push(void* aItem);
|
||||
|
||||
/**
|
||||
* Inserts new member at the front of the deque.
|
||||
*
|
||||
* @param item to store in deque
|
||||
* @return *this
|
||||
*/
|
||||
nsDeque& PushFront(void* aItem);
|
||||
|
||||
/**
|
||||
* Remove and return the last item in the container.
|
||||
*
|
||||
* @return the item that was the last item in container
|
||||
*/
|
||||
void* Pop();
|
||||
|
||||
/**
|
||||
* Remove and return the first item in the container.
|
||||
*
|
||||
* @return the item that was first item in container
|
||||
*/
|
||||
void* PopFront();
|
||||
|
||||
/**
|
||||
* Retrieve the bottom item without removing it.
|
||||
*
|
||||
* @return the first item in container
|
||||
*/
|
||||
|
||||
void* Peek();
|
||||
/**
|
||||
* Return topmost item without removing it.
|
||||
*
|
||||
* @return the first item in container
|
||||
*/
|
||||
void* PeekFront();
|
||||
|
||||
/**
|
||||
* Retrieve the i'th member from the deque without removing it.
|
||||
*
|
||||
* @param index of desired item
|
||||
* @return i'th element in list
|
||||
*/
|
||||
void* ObjectAt(int aIndex) const;
|
||||
|
||||
/**
|
||||
* Remove all items from container without destroying them.
|
||||
*
|
||||
* @return *this
|
||||
*/
|
||||
nsDeque& Empty();
|
||||
|
||||
/**
|
||||
* Remove and delete all items from container.
|
||||
* Deletes are handled by the deallocator nsDequeFunctor
|
||||
* which is specified at deque construction.
|
||||
*
|
||||
* @return *this
|
||||
*/
|
||||
nsDeque& Erase();
|
||||
|
||||
/**
|
||||
* Creates a new iterator, pointing to the first
|
||||
* item in the deque.
|
||||
*
|
||||
* @return new dequeIterator
|
||||
*/
|
||||
nsDequeIterator Begin() const;
|
||||
|
||||
/**
|
||||
* Creates a new iterator, pointing to the last
|
||||
* item in the deque.
|
||||
*
|
||||
* @return new dequeIterator
|
||||
*/
|
||||
nsDequeIterator End() const;
|
||||
|
||||
void* Last() const;
|
||||
/**
|
||||
* Call this method when you want to iterate all the
|
||||
* members of the container, passing a functor along
|
||||
* to call your code.
|
||||
*
|
||||
* @param aFunctor object to call for each member
|
||||
* @return *this
|
||||
*/
|
||||
void ForEach(nsDequeFunctor& aFunctor) const;
|
||||
|
||||
/**
|
||||
* Call this method when you want to iterate all the
|
||||
* members of the container, calling the functor you
|
||||
* passed with each member. This process will interrupt
|
||||
* if your function returns non 0 to this method.
|
||||
*
|
||||
* @param aFunctor object to call for each member
|
||||
* @return first nonzero result of aFunctor or 0.
|
||||
*/
|
||||
const void* FirstThat(nsDequeFunctor& aFunctor) const;
|
||||
|
||||
void SetDeallocator(nsDequeFunctor* aDeallocator);
|
||||
|
||||
protected:
|
||||
PRInt32 mSize;
|
||||
PRInt32 mCapacity;
|
||||
PRInt32 mOrigin;
|
||||
nsDequeFunctor* mDeallocator;
|
||||
void* mBuffer[8];
|
||||
void** mData;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Simple default constructor (PRIVATE)
|
||||
*/
|
||||
nsDeque();
|
||||
|
||||
/**
|
||||
* Copy constructor (PRIVATE)
|
||||
*
|
||||
* @param another deque
|
||||
*/
|
||||
nsDeque(const nsDeque& other);
|
||||
|
||||
/**
|
||||
* Deque assignment operator (PRIVATE)
|
||||
*
|
||||
* @param another deque
|
||||
* @return *this
|
||||
*/
|
||||
nsDeque& operator=(const nsDeque& anOther);
|
||||
|
||||
PRInt32 GrowCapacity();
|
||||
};
|
||||
|
||||
/******************************************************
|
||||
* Here comes the nsDequeIterator class...
|
||||
******************************************************/
|
||||
|
||||
class NS_COM nsDequeIterator {
|
||||
public:
|
||||
/**
|
||||
* DequeIterator is an object that knows how to iterate
|
||||
* (forward and backward) through a Deque. Normally,
|
||||
* you don't need to do this, but there are some special
|
||||
* cases where it is pretty handy.
|
||||
*
|
||||
* One warning: the iterator is not bound to an item,
|
||||
* it is bound to an index, so if you insert or remove
|
||||
* from the beginning while using an iterator
|
||||
* (which is not recommended) then the iterator will
|
||||
* point to a different item. @see GetCurrent()
|
||||
*
|
||||
* Here you go.
|
||||
*
|
||||
* @param aQueue is the deque object to be iterated
|
||||
* @param aIndex is the starting position for your iteration
|
||||
*/
|
||||
nsDequeIterator(const nsDeque& aQueue, int aIndex=0);
|
||||
|
||||
/**
|
||||
* Create a copy of a DequeIterator
|
||||
*
|
||||
* @param aCopy is another iterator to copy from
|
||||
*/
|
||||
nsDequeIterator(const nsDequeIterator& aCopy);
|
||||
|
||||
/**
|
||||
* Moves iterator to first element in the deque
|
||||
* @return *this
|
||||
*/
|
||||
nsDequeIterator& First();
|
||||
|
||||
/**
|
||||
* Standard assignment operator for dequeiterator
|
||||
* @param aCopy is another iterator to copy from
|
||||
* @return *this
|
||||
*/
|
||||
nsDequeIterator& operator=(const nsDequeIterator& aCopy);
|
||||
|
||||
/**
|
||||
* preform ! operation against two iterators to test for equivalence
|
||||
* (or lack thereof)!
|
||||
*
|
||||
* @param aIter is the object to be compared to
|
||||
* @return TRUE if NOT equal.
|
||||
*/
|
||||
PRBool operator!=(nsDequeIterator& aIter);
|
||||
|
||||
/**
|
||||
* Compare two iterators for increasing order.
|
||||
*
|
||||
* @param aIter is the other iterator to be compared to
|
||||
* @return TRUE if this object points to an element before
|
||||
* the element pointed to by aIter.
|
||||
* FALSE if this and aIter are not iterating over
|
||||
* the same deque.
|
||||
*/
|
||||
PRBool operator<(nsDequeIterator& aIter);
|
||||
|
||||
/**
|
||||
* Compare two iterators for equivalence.
|
||||
*
|
||||
* @param aIter is the other iterator to be compared to
|
||||
* @return TRUE if EQUAL
|
||||
*/
|
||||
PRBool operator==(nsDequeIterator& aIter);
|
||||
|
||||
/**
|
||||
* Compare two iterators for non strict decreasing order.
|
||||
*
|
||||
* @param aIter is the other iterator to be compared to
|
||||
* @return TRUE if this object points to the same element, or
|
||||
* an element after the element pointed to by aIter.
|
||||
* FALSE if this and aIter are not iterating over
|
||||
* the same deque.
|
||||
*/
|
||||
PRBool operator>=(nsDequeIterator& aIter);
|
||||
|
||||
/**
|
||||
* Pre-increment operator
|
||||
* Iterator will advance one index towards the end.
|
||||
*
|
||||
* @return object_at(++index)
|
||||
*/
|
||||
void* operator++();
|
||||
|
||||
/**
|
||||
* Post-increment operator
|
||||
* Iterator will advance one index towards the end.
|
||||
*
|
||||
* @param param is ignored
|
||||
* @return object_at(mIndex++)
|
||||
*/
|
||||
void* operator++(int);
|
||||
|
||||
/**
|
||||
* Pre-decrement operator
|
||||
* Iterator will advance one index towards the beginning.
|
||||
*
|
||||
* @return object_at(--index)
|
||||
*/
|
||||
void* operator--();
|
||||
|
||||
/**
|
||||
* Post-decrement operator
|
||||
* Iterator will advance one index towards the beginning.
|
||||
*
|
||||
* @param param is ignored
|
||||
* @return object_at(index--)
|
||||
*/
|
||||
void* operator--(int);
|
||||
|
||||
/**
|
||||
* Retrieve the the iterator's notion of current node.
|
||||
*
|
||||
* Note that the iterator floats, so you don't need to do:
|
||||
* <code>++iter; aDeque.PopFront();</code>
|
||||
* Unless you actually want your iterator to jump 2 positions
|
||||
* relative to its origin.
|
||||
*
|
||||
* Picture: [1 2i 3 4]
|
||||
* PopFront()
|
||||
* Picture: [2 3i 4]
|
||||
* Note that I still happily points to object at the second index.
|
||||
*
|
||||
* @return object at i'th index
|
||||
*/
|
||||
void* GetCurrent();
|
||||
|
||||
/**
|
||||
* Call this method when you want to iterate all the
|
||||
* members of the container, passing a functor along
|
||||
* to call your code.
|
||||
*
|
||||
* @param aFunctor object to call for each member
|
||||
* @return *this
|
||||
*/
|
||||
void ForEach(nsDequeFunctor& aFunctor) const;
|
||||
|
||||
/**
|
||||
* Call this method when you want to iterate all the
|
||||
* members of the container, calling the functor you
|
||||
* passed with each member. This process will interrupt
|
||||
* if your function returns non 0 to this method.
|
||||
*
|
||||
* @param aFunctor object to call for each member
|
||||
* @return first nonzero result of aFunctor or 0.
|
||||
*/
|
||||
const void* FirstThat(nsDequeFunctor& aFunctor) const;
|
||||
|
||||
protected:
|
||||
|
||||
PRInt32 mIndex;
|
||||
const nsDeque& mDeque;
|
||||
};
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче