1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set cindent tabstop=4 expandtab shiftwidth=4: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * The Mozilla Foundation.
20 * Portions created by the Initial Developer are Copyright (C) 2006
21 * the Initial Developer. All Rights Reserved.
24 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 // This file implements a garbage-cycle collector based on the paper
43 // Concurrent Cycle Collection in Reference Counted Systems
44 // Bacon & Rajan (2001), ECOOP 2001 / Springer LNCS vol 2072
46 // We are not using the concurrent or acyclic cases of that paper; so
47 // the green, red and orange colors are not used.
49 // The collector is based on tracking pointers of four colors:
51 // Black nodes are definitely live. If we ever determine a node is
52 // black, it's ok to forget about, drop from our records.
54 // White nodes are definitely garbage cycles. Once we finish with our
55 // scanning, we unlink all the white nodes and expect that by
56 // unlinking them they will self-destruct (since a garbage cycle is
57 // only keeping itself alive with internal links, by definition).
59 // Grey nodes are being scanned. Nodes that turn grey will turn
60 // either black if we determine that they're live, or white if we
61 // determine that they're a garbage cycle. After the main collection
62 // algorithm there should be no grey nodes.
64 // Purple nodes are *candidates* for being scanned. They are nodes we
65 // haven't begun scanning yet because they're not old enough, or we're
66 // still partway through the algorithm.
68 // XPCOM objects participating in garbage-cycle collection are obliged
69 // to inform us when they ought to turn purple; that is, when their
70 // refcount transitions from N+1 -> N, for nonzero N. Furthermore we
71 // require that *after* an XPCOM object has informed us of turning
72 // purple, they will tell us when they either transition back to being
73 // black (incremented refcount) or are ultimately deleted.
78 // An XPCOM object is either scan-safe or scan-unsafe, purple-safe or
81 // An object is scan-safe if:
83 // - It can be QI'ed to |nsXPCOMCycleCollectionParticipant|, though this
84 // operation loses ISupports identity (like nsIClassInfo).
85 // - The operation |traverse| on the resulting
86 // nsXPCOMCycleCollectionParticipant does not cause *any* refcount
87 // adjustment to occur (no AddRef / Release calls).
89 // An object is purple-safe if it satisfies the following properties:
91 // - The object is scan-safe.
92 // - If the object calls |nsCycleCollector::suspect(this)|,
93 // it will eventually call |nsCycleCollector::forget(this)|,
94 // exactly once per call to |suspect|, before being destroyed.
96 // When we receive a pointer |ptr| via
97 // |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We
98 // can check the scan-safety, but have no way to ensure the
99 // purple-safety; objects must obey, or else the entire system falls
100 // apart. Don't involve an object in this scheme if you can't
101 // guarantee its purple-safety.
103 // When we have a scannable set of purple nodes ready, we begin
104 // our walks. During the walks, the nodes we |traverse| should only
105 // feed us more scan-safe nodes, and should not adjust the refcounts
108 // We do not |AddRef| or |Release| any objects during scanning. We
109 // rely on purple-safety of the roots that call |suspect| and
110 // |forget| to hold, such that we will forget about a purple pointer
111 // before it is destroyed. The pointers that are merely scan-safe,
112 // we hold only for the duration of scanning, and there should be no
113 // objects released from the scan-safe set during the scan (there
114 // should be no threads involved).
116 // We *do* call |AddRef| and |Release| on every white object, on
117 // either side of the calls to |Unlink|. This keeps the set of white
118 // objects alive during the unlinking.
121 #if !defined(__MINGW32__) && !defined(WINCE)
128 #include "nsCycleCollectionParticipant.h"
129 #include "nsIProgrammingLanguage.h"
130 #include "nsBaseHashtable.h"
131 #include "nsHashKeys.h"
133 #include "nsCycleCollector.h"
134 #include "nsThreadUtils.h"
139 #include "nsPrintfCString.h"
140 #include "nsTArray.h"
141 #include "nsIObserverService.h"
142 #include "nsIConsoleService.h"
143 #include "nsServiceManagerUtils.h"
144 #include "nsThreadUtils.h"
145 #include "nsTPtrArray.h"
146 #include "nsVoidArray.h" // for nsCStringArray
155 #define DEFAULT_SHUTDOWN_COLLECTIONS 5
157 #define SHUTDOWN_COLLECTIONS(params) params.mShutdownCollections
159 #define SHUTDOWN_COLLECTIONS(params) DEFAULT_SHUTDOWN_COLLECTIONS
162 // Various parameters of this collector can be tuned using environment
165 struct nsCycleCollectorParams
172 PRBool mFaultIsFatal
;
175 PRUint32 mShutdownCollections
;
180 nsCycleCollectorParams() :
182 mDoNothing (PR_GetEnv("XPCOM_CC_DO_NOTHING") != NULL
),
183 mReportStats (PR_GetEnv("XPCOM_CC_REPORT_STATS") != NULL
),
184 mHookMalloc (PR_GetEnv("XPCOM_CC_HOOK_MALLOC") != NULL
),
185 mDrawGraphs (PR_GetEnv("XPCOM_CC_DRAW_GRAPHS") != NULL
),
186 mFaultIsFatal (PR_GetEnv("XPCOM_CC_FAULT_IS_FATAL") != NULL
),
187 mLogPointers (PR_GetEnv("XPCOM_CC_LOG_POINTERS") != NULL
),
189 mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS
),
191 mDoNothing (PR_FALSE
),
194 // The default number of collections to "age" candidate
195 // pointers in the purple buffer before we decide that any
196 // garbage cycle they're in has stabilized and we want to
197 // consider scanning it.
199 // Making this number smaller causes:
200 // - More time to be spent in the collector (bad)
201 // - Less delay between forming garbage and collecting it (good)
206 char *s
= PR_GetEnv("XPCOM_CC_SCAN_DELAY");
208 PR_sscanf(s
, "%d", &mScanDelay
);
209 s
= PR_GetEnv("XPCOM_CC_SHUTDOWN_COLLECTIONS");
211 PR_sscanf(s
, "%d", &mShutdownCollections
);
217 // Various operations involving the collector are recorded in a
218 // statistics table. These are for diagnostics.
220 struct nsCycleCollectorStats
223 PRUint32 mSuccessfulQI
;
225 PRUint32 mVisitedNode
;
226 PRUint32 mWalkedGraph
;
227 PRUint32 mCollectedBytes
;
229 PRUint32 mFreedBytes
;
231 PRUint32 mSetColorGrey
;
232 PRUint32 mSetColorBlack
;
233 PRUint32 mSetColorWhite
;
235 PRUint32 mFailedUnlink
;
236 PRUint32 mCollectedNode
;
237 PRUint32 mBumpGeneration
;
238 PRUint32 mZeroGeneration
;
240 PRUint32 mSuspectNode
;
242 PRUint32 mForgetNode
;
243 PRUint32 mFreedWhilePurple
;
245 PRUint32 mCollection
;
247 nsCycleCollectorStats()
249 memset(this, 0, sizeof(nsCycleCollectorStats
));
254 fprintf(stderr
, "\f\n");
255 #define DUMP(entry) fprintf(stderr, "%30.30s: %-20.20d\n", #entry, entry)
261 DUMP(mCollectedBytes
);
266 DUMP(mSetColorBlack
);
267 DUMP(mSetColorWhite
);
270 DUMP(mCollectedNode
);
271 DUMP(mBumpGeneration
);
272 DUMP(mZeroGeneration
);
277 DUMP(mFreedWhilePurple
);
286 static PRBool
nsCycleCollector_shouldSuppress(nsISupports
*s
);
287 static void InitMemHook(void);
290 ////////////////////////////////////////////////////////////////////////
292 ////////////////////////////////////////////////////////////////////////
299 // EdgePool allocates arrays of void*, primarily to hold PtrInfo*.
300 // However, at the end of a block, the last two pointers are a null
301 // and then a void** pointing to the next block. This allows
302 // EdgePool::Iterators to be a single word but still capable of crossing
307 mSentinelAndBlocks
[0].block
= nsnull
;
308 mSentinelAndBlocks
[1].block
= nsnull
;
313 NS_ASSERTION(!mSentinelAndBlocks
[0].block
&&
314 !mSentinelAndBlocks
[1].block
,
315 "Didn't call Clear()?");
322 Block
*next
= b
->Next();
327 mSentinelAndBlocks
[0].block
= nsnull
;
328 mSentinelAndBlocks
[1].block
= nsnull
;
333 union PtrInfoOrBlock
{
334 // Use a union to avoid reinterpret_cast and the ensuing
335 // potential aliasing bugs.
340 enum { BlockSize
= 64 * 1024 };
342 PtrInfoOrBlock mPointers
[BlockSize
];
344 mPointers
[BlockSize
- 2].block
= nsnull
; // sentinel
345 mPointers
[BlockSize
- 1].block
= nsnull
; // next block pointer
348 { return mPointers
[BlockSize
- 1].block
; }
349 PtrInfoOrBlock
* Start()
350 { return &mPointers
[0]; }
351 PtrInfoOrBlock
* End()
352 { return &mPointers
[BlockSize
- 2]; }
355 // Store the null sentinel so that we can have valid iterators
356 // before adding any edges and without adding any blocks.
357 PtrInfoOrBlock mSentinelAndBlocks
[2];
359 Block
*& Blocks() { return mSentinelAndBlocks
[1].block
; }
365 Iterator() : mPointer(nsnull
) {}
366 Iterator(PtrInfoOrBlock
*aPointer
) : mPointer(aPointer
) {}
367 Iterator(const Iterator
& aOther
) : mPointer(aOther
.mPointer
) {}
369 Iterator
& operator++()
371 if (mPointer
->ptrInfo
== nsnull
) {
372 // Null pointer is a sentinel for link to the next block.
373 mPointer
= (mPointer
+ 1)->block
->mPointers
;
379 PtrInfo
* operator*() const
381 if (mPointer
->ptrInfo
== nsnull
) {
382 // Null pointer is a sentinel for link to the next block.
383 return (mPointer
+ 1)->block
->mPointers
->ptrInfo
;
385 return mPointer
->ptrInfo
;
387 PRBool
operator==(const Iterator
& aOther
) const
388 { return mPointer
== aOther
.mPointer
; }
389 PRBool
operator!=(const Iterator
& aOther
) const
390 { return mPointer
!= aOther
.mPointer
; }
393 PtrInfoOrBlock
*mPointer
;
397 friend class Builder
;
400 Builder(EdgePool
&aPool
)
401 : mCurrent(&aPool
.mSentinelAndBlocks
[0]),
402 mBlockEnd(&aPool
.mSentinelAndBlocks
[0]),
403 mNextBlockPtr(&aPool
.Blocks())
407 Iterator
Mark() { return Iterator(mCurrent
); }
409 void Add(PtrInfo
* aEdge
) {
410 if (mCurrent
== mBlockEnd
) {
411 Block
*b
= new Block();
413 // This means we just won't collect (some) cycles.
414 NS_NOTREACHED("out of memory, ignoring edges");
418 mCurrent
= b
->Start();
419 mBlockEnd
= b
->End();
420 mNextBlockPtr
= &b
->Next();
422 (mCurrent
++)->ptrInfo
= aEdge
;
425 // mBlockEnd points to space for null sentinel
426 PtrInfoOrBlock
*mCurrent
, *mBlockEnd
;
427 Block
**mNextBlockPtr
;
434 struct ReversedEdge
{
436 nsCString
*mEdgeName
;
443 enum NodeColor
{ black
, white
, grey
};
445 // This structure should be kept as small as possible; we may expect
446 // a million of them to be allocated and touched repeatedly during
447 // each cycle collection.
452 nsCycleCollectionParticipant
*mParticipant
;
454 PRUint32 mInternalRefs
: 30;
456 EdgePool::Iterator mFirstChild
; // first
457 EdgePool::Iterator mLastChild
; // one after last
464 // For finding roots in ExplainLiveExpectedGarbage (when there are
465 // missing calls to suspect or failures to unlink).
466 PRUint32 mSCCIndex
; // strongly connected component
468 // For finding roots in ExplainLiveExpectedGarbage (when nodes
469 // expected to be garbage are black).
470 ReversedEdge
* mReversedEdges
; // linked list
471 PtrInfo
* mShortestPathToExpectedGarbage
;
472 nsCString
* mShortestPathToExpectedGarbageEdgeName
;
474 nsCStringArray mEdgeNames
;
477 PtrInfo(void *aPointer
, nsCycleCollectionParticipant
*aParticipant
478 IF_DEBUG_CC_PARAM(PRUint32 aLangID
)
480 : mPointer(aPointer
),
481 mParticipant(aParticipant
),
492 mReversedEdges(nsnull
),
493 mShortestPathToExpectedGarbage(nsnull
),
494 mShortestPathToExpectedGarbageEdgeName(nsnull
)
502 mEdgeNames
.~nsCStringArray();
506 // Allow NodePool::Block's constructor to compile.
508 NS_NOTREACHED("should never be called");
513 * A structure designed to be used like a linked list of PtrInfo, except
514 * that allocates the PtrInfo 32K-at-a-time.
519 enum { BlockSize
= 32 * 1024 }; // could be int template parameter
522 // We create and destroy Block using NS_Alloc/NS_Free rather
523 // than new and delete to avoid calling its constructor and
525 Block() { NS_NOTREACHED("should never be called"); }
526 ~Block() { NS_NOTREACHED("should never be called"); }
529 PtrInfo mEntries
[BlockSize
];
541 NS_ASSERTION(!mBlocks
, "Didn't call Clear()?");
548 Enumerator
queue(*this);
549 while (!queue
.IsDone()) {
550 queue
.GetNext()->Destroy();
566 friend class Builder
;
569 Builder(NodePool
& aPool
)
570 : mNextBlock(&aPool
.mBlocks
),
574 NS_ASSERTION(aPool
.mBlocks
== nsnull
&& aPool
.mLast
== nsnull
,
577 PtrInfo
*Add(void *aPointer
, nsCycleCollectionParticipant
*aParticipant
578 IF_DEBUG_CC_PARAM(PRUint32 aLangID
)
581 if (mNext
== mBlockEnd
) {
583 if (!(*mNextBlock
= block
=
584 static_cast<Block
*>(NS_Alloc(sizeof(Block
)))))
586 mNext
= block
->mEntries
;
587 mBlockEnd
= block
->mEntries
+ BlockSize
;
588 block
->mNext
= nsnull
;
589 mNextBlock
= &block
->mNext
;
591 return new (mNext
++) PtrInfo(aPointer
, aParticipant
592 IF_DEBUG_CC_PARAM(aLangID
)
602 friend class Enumerator
;
605 Enumerator(NodePool
& aPool
)
606 : mFirstBlock(aPool
.mBlocks
),
614 PRBool
IsDone() const
616 return mNext
== mLast
;
621 NS_ASSERTION(!IsDone(), "calling GetNext when done");
622 if (mNext
== mBlockEnd
) {
623 Block
*nextBlock
= mCurBlock
? mCurBlock
->mNext
: mFirstBlock
;
624 mNext
= nextBlock
->mEntries
;
625 mBlockEnd
= mNext
+ BlockSize
;
626 mCurBlock
= nextBlock
;
631 Block
*mFirstBlock
, *mCurBlock
;
632 // mNext is the next value we want to return, unless mNext == mBlockEnd
633 // NB: mLast is a reference to allow enumerating while building!
634 PtrInfo
*mNext
, *mBlockEnd
, *&mLast
;
643 class GCGraphBuilder
;
651 ReversedEdge
*mReversedEdges
;
654 GCGraph() : mRootCount(0) {
660 // XXX Would be nice to have an nsHashSet<KeyType> API that has
661 // Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
662 typedef nsTHashtable
<nsVoidPtrHashKey
> PointerSet
;
663 typedef nsBaseHashtable
<nsVoidPtrHashKey
, PRUint32
, PRUint32
>
664 PointerSetWithGeneration
;
668 WriteGraph(FILE *stream
, GCGraph
&graph
, const void *redPtr
);
671 struct nsPurpleBuffer
674 #define ASSOCIATIVITY 2
675 #define INDEX_LOW_BIT 6
676 #define N_INDEX_BITS 13
678 #define N_ENTRIES (1 << N_INDEX_BITS)
679 #define N_POINTERS (N_ENTRIES * ASSOCIATIVITY)
680 #define TOTAL_BYTES (N_POINTERS * PR_BYTES_PER_WORD)
681 #define INDEX_MASK PR_BITMASK(N_INDEX_BITS)
682 #define POINTER_INDEX(P) ((((PRUword)P) >> INDEX_LOW_BIT) & (INDEX_MASK))
684 #if (INDEX_LOW_BIT + N_INDEX_BITS > (8 * PR_BYTES_PER_WORD))
685 #error "index bit overflow"
688 // This class serves as a generational wrapper around a pldhash
689 // table: a subset of generation zero lives in mCache, the
690 // remainder spill into the mBackingStore hashtable. The idea is
691 // to get a higher hit rate and greater locality of reference for
692 // generation zero, in which the vast majority of suspect/forget
693 // calls annihilate one another.
695 nsCycleCollectorParams
&mParams
;
697 nsCycleCollectorStats
&mStats
;
699 void* mCache
[N_POINTERS
];
701 PointerSetWithGeneration mBackingStore
;
704 nsPurpleBuffer(nsCycleCollectorParams
¶ms
,
705 nsCycleCollectorStats
&stats
)
713 nsPurpleBuffer(nsCycleCollectorParams
¶ms
)
723 memset(mCache
, 0, sizeof(mCache
));
724 mBackingStore
.Clear();
729 memset(mCache
, 0, sizeof(mCache
));
730 mBackingStore
.Init();
733 void BumpGeneration();
734 void SelectAgedPointers(GCGraphBuilder
&builder
);
736 PRBool
Exists(void *p
)
738 PRUint32 idx
= POINTER_INDEX(p
);
739 for (PRUint32 i
= 0; i
< ASSOCIATIVITY
; ++i
) {
740 if (mCache
[idx
+i
] == p
)
744 return mBackingStore
.Get(p
, &gen
);
749 PRUint32 idx
= POINTER_INDEX(p
);
750 for (PRUint32 i
= 0; i
< ASSOCIATIVITY
; ++i
) {
751 if (!mCache
[idx
+i
]) {
764 PRUint32 idx
= POINTER_INDEX(p
);
765 for (PRUint32 i
= 0; i
< ASSOCIATIVITY
; ++i
) {
766 if (mCache
[idx
+i
] == p
) {
767 mCache
[idx
+i
] = (void*)0;
771 mBackingStore
.Remove(p
);
774 void SpillOne(void* &p
)
776 mBackingStore
.Put(p
, mCurrGen
);
782 for (PRUint32 i
= 0; i
< N_POINTERS
; ++i
) {
791 PRUint32 count
= mBackingStore
.Count();
792 for (PRUint32 i
= 0; i
< N_POINTERS
; ++i
) {
801 static PLDHashOperator
802 zeroGenerationCallback(const void* ptr
,
803 PRUint32
& generation
,
807 nsPurpleBuffer
*purp
= static_cast<nsPurpleBuffer
*>(userArg
);
808 purp
->mStats
.mZeroGeneration
++;
811 return PL_DHASH_NEXT
;
814 void nsPurpleBuffer::BumpGeneration()
817 if (mCurrGen
== 0xffffffff) {
818 mBackingStore
.Enumerate(zeroGenerationCallback
, this);
824 mStats
.mBumpGeneration
++;
829 SufficientlyAged(PRUint32 generation
, nsPurpleBuffer
*p
)
831 return generation
+ p
->mParams
.mScanDelay
< p
->mCurrGen
;
834 struct CallbackClosure
836 CallbackClosure(nsPurpleBuffer
*aPurpleBuffer
, GCGraphBuilder
&aBuilder
)
837 : mPurpleBuffer(aPurpleBuffer
),
841 nsPurpleBuffer
*mPurpleBuffer
;
842 GCGraphBuilder
&mBuilder
;
846 AddPurpleRoot(GCGraphBuilder
&builder
, nsISupports
*root
);
848 static PLDHashOperator
849 ageSelectionCallback(const void* ptr
,
850 PRUint32
& generation
,
853 CallbackClosure
*closure
= static_cast<CallbackClosure
*>(userArg
);
854 if (SufficientlyAged(generation
, closure
->mPurpleBuffer
) &&
855 AddPurpleRoot(closure
->mBuilder
,
856 static_cast<nsISupports
*>(const_cast<void*>(ptr
))))
857 return PL_DHASH_REMOVE
;
859 return PL_DHASH_NEXT
;
863 nsPurpleBuffer::SelectAgedPointers(GCGraphBuilder
&aBuilder
)
865 // Rely on our caller having done a BumpGeneration first, which in
866 // turn calls SpillAll.
867 CallbackClosure
closure(this, aBuilder
);
868 mBackingStore
.Enumerate(ageSelectionCallback
, &closure
);
873 ////////////////////////////////////////////////////////////////////////
874 // Implement the LanguageRuntime interface for C++/XPCOM
875 ////////////////////////////////////////////////////////////////////////
878 struct nsCycleCollectionXPCOMRuntime
:
879 public nsCycleCollectionLanguageRuntime
881 nsresult
BeginCycleCollection(nsCycleCollectionTraversalCallback
&cb
)
886 nsresult
FinishCycleCollection()
891 inline nsCycleCollectionParticipant
*ToParticipant(void *p
);
894 virtual void PrintAllReferencesTo(void *p
) {}
898 struct nsCycleCollector
900 PRBool mCollectionInProgress
;
901 PRBool mScanInProgress
;
902 PRBool mFollowupCollection
;
903 PRUint32 mCollectedObjects
;
905 nsCycleCollectionLanguageRuntime
*mRuntimes
[nsIProgrammingLanguage::MAX
+1];
906 nsCycleCollectionXPCOMRuntime mXPCOMRuntime
;
910 nsCycleCollectorParams mParams
;
912 nsTPtrArray
<PtrInfo
> *mWhiteNodes
;
913 PRUint32 mWhiteNodeCount
;
915 nsPurpleBuffer mPurpleBuf
;
917 void RegisterRuntime(PRUint32 langID
,
918 nsCycleCollectionLanguageRuntime
*rt
);
919 void ForgetRuntime(PRUint32 langID
);
921 void SelectPurple(GCGraphBuilder
&builder
);
922 void MarkRoots(GCGraphBuilder
&builder
);
925 PRBool
CollectWhite(); // returns whether anything was collected
930 PRBool
Suspect(nsISupports
*n
);
931 PRBool
Forget(nsISupports
*n
);
932 PRUint32
Collect(PRUint32 aTryCollections
= 1);
933 PRBool
BeginCollection();
934 PRBool
FinishCollection();
935 PRUint32
SuspectedCount();
940 mGraph
.mNodes
.Clear();
941 mGraph
.mEdges
.Clear();
942 mGraph
.mRootCount
= 0;
946 nsCycleCollectorStats mStats
;
950 void MaybeDrawGraphs();
951 void Allocated(void *n
, size_t sz
);
954 void ExplainLiveExpectedGarbage();
955 PRBool
CreateReversedEdges();
956 void DestroyReversedEdges();
957 void ShouldBeFreed(nsISupports
*n
);
958 void WasFreed(nsISupports
*n
);
959 PointerSet mExpectedGarbage
;
967 void DoWalk(nsDeque
&aQueue
);
970 void Walk(PtrInfo
*s0
);
971 void WalkFromRoots(GCGraph
&aGraph
);
973 // Provided by concrete walker subtypes.
974 virtual PRBool
ShouldVisitNode(PtrInfo
const *pi
) = 0;
975 virtual void VisitNode(PtrInfo
*pi
) = 0;
979 ////////////////////////////////////////////////////////////////////////
980 // The static collector object
981 ////////////////////////////////////////////////////////////////////////
984 static nsCycleCollector
*sCollector
= nsnull
;
987 ////////////////////////////////////////////////////////////////////////
989 ////////////////////////////////////////////////////////////////////////
991 class CCRunnableFaultReport
: public nsRunnable
{
993 CCRunnableFaultReport(const nsCString
& report
)
995 CopyUTF8toUTF16(report
, mReport
);
999 nsCOMPtr
<nsIObserverService
> obs
=
1000 do_GetService(NS_OBSERVERSERVICE_CONTRACTID
);
1002 obs
->NotifyObservers(nsnull
, "cycle-collector-fault",
1006 nsCOMPtr
<nsIConsoleService
> cons
=
1007 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1009 cons
->LogStringMessage(mReport
.get());
1019 Fault(const char *msg
, const void *ptr
=nsnull
)
1022 // This should be nearly impossible, but just in case.
1026 if (sCollector
->mParams
.mFaultIsFatal
) {
1029 printf("Fatal fault in cycle collector: %s (ptr: %p)\n", msg
, ptr
);
1031 printf("Fatal fault in cycle collector: %s\n", msg
);
1034 if (sCollector
->mGraph
.mRootCount
> 0) {
1037 const char fname
[] = "c:\\fault-graph.dot";
1039 const char fname
[] = "/tmp/fault-graph.dot";
1041 printf("depositing faulting cycle-collection graph in %s\n", fname
);
1042 stream
= fopen(fname
, "w+");
1043 WriteGraph(stream
, sCollector
->mGraph
, ptr
);
1051 nsPrintfCString
str(256, "Fault in cycle collector: %s (ptr: %p)\n",
1053 NS_NOTREACHED(str
.get());
1055 // When faults are not fatal, we assume we're running in a
1056 // production environment and we therefore want to disable the
1057 // collector on a fault. This will unfortunately cause the browser
1058 // to leak pretty fast wherever creates cyclical garbage, but it's
1059 // probably a better user experience than crashing. Besides, we
1060 // *should* never hit a fault.
1062 sCollector
->mParams
.mDoNothing
= PR_TRUE
;
1064 // Report to observers off an event so we don't run JS under GC
1065 // (which is where we might be right now).
1066 nsCOMPtr
<nsIRunnable
> ev
= new CCRunnableFaultReport(str
);
1067 NS_DispatchToCurrentThread(ev
);
1072 Fault(const char *msg
, PtrInfo
*pi
)
1074 printf("Fault in cycle collector: %s\n"
1075 " while operating on pointer %p %s\n",
1076 msg
, pi
->mPointer
, pi
->mName
);
1077 if (pi
->mInternalRefs
) {
1078 printf(" which has internal references from:\n");
1079 NodePool::Enumerator
queue(sCollector
->mGraph
.mNodes
);
1080 while (!queue
.IsDone()) {
1081 PtrInfo
*ppi
= queue
.GetNext();
1082 for (EdgePool::Iterator e
= ppi
->mFirstChild
, e_end
= ppi
->mLastChild
;
1085 printf(" %p %s\n", ppi
->mPointer
, ppi
->mName
);
1091 Fault(msg
, pi
->mPointer
);
1095 Fault(const char *msg
, PtrInfo
*pi
)
1097 Fault(msg
, pi
->mPointer
);
1103 static nsISupports
*
1104 canonicalize(nsISupports
*in
)
1106 nsCOMPtr
<nsISupports
> child
;
1107 in
->QueryInterface(NS_GET_IID(nsCycleCollectionISupports
),
1108 getter_AddRefs(child
));
1113 ToParticipant(nsISupports
*s
, nsXPCOMCycleCollectionParticipant
**cp
)
1115 // We use QI to move from an nsISupports to an
1116 // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
1117 // object that implements traversal and unlinking logic for the nsISupports
1119 CallQueryInterface(s
, cp
);
1122 ++sCollector
->mStats
.mSuccessfulQI
;
1124 ++sCollector
->mStats
.mFailedQI
;
1128 nsCycleCollectionParticipant
*
1129 nsCycleCollectionXPCOMRuntime::ToParticipant(void *p
)
1131 nsXPCOMCycleCollectionParticipant
*cp
;
1132 ::ToParticipant(static_cast<nsISupports
*>(p
), &cp
);
1138 GraphWalker::Walk(PtrInfo
*s0
)
1146 GraphWalker::WalkFromRoots(GCGraph
& aGraph
)
1149 NodePool::Enumerator
etor(aGraph
.mNodes
);
1150 for (PRUint32 i
= 0; i
< aGraph
.mRootCount
; ++i
) {
1151 queue
.Push(etor
.GetNext());
1157 GraphWalker::DoWalk(nsDeque
&aQueue
)
1159 // Use a aQueue to match the breadth-first traversal used when we
1160 // built the graph, for hopefully-better locality.
1161 while (aQueue
.GetSize() > 0) {
1162 PtrInfo
*pi
= static_cast<PtrInfo
*>(aQueue
.PopFront());
1164 if (this->ShouldVisitNode(pi
)) {
1165 this->VisitNode(pi
);
1166 for (EdgePool::Iterator child
= pi
->mFirstChild
,
1167 child_end
= pi
->mLastChild
;
1168 child
!= child_end
; ++child
) {
1169 aQueue
.Push(*child
);
1175 sCollector
->mStats
.mWalkedGraph
++;
1180 ////////////////////////////////////////////////////////////////////////
1181 // Bacon & Rajan's |MarkRoots| routine.
1182 ////////////////////////////////////////////////////////////////////////
1184 struct PtrToNodeEntry
: public PLDHashEntryHdr
1186 // The key is mNode->mPointer
1191 PtrToNodeMatchEntry(PLDHashTable
*table
,
1192 const PLDHashEntryHdr
*entry
,
1195 const PtrToNodeEntry
*n
= static_cast<const PtrToNodeEntry
*>(entry
);
1196 return n
->mNode
->mPointer
== key
;
1199 static PLDHashTableOps PtrNodeOps
= {
1202 PL_DHashVoidPtrKeyStub
,
1203 PtrToNodeMatchEntry
,
1204 PL_DHashMoveEntryStub
,
1205 PL_DHashClearEntryStub
,
1206 PL_DHashFinalizeStub
,
1210 class GCGraphBuilder
: public nsCycleCollectionTraversalCallback
1213 NodePool::Builder mNodeBuilder
;
1214 EdgePool::Builder mEdgeBuilder
;
1215 PLDHashTable mPtrToNodeMap
;
1217 nsCycleCollectionLanguageRuntime
**mRuntimes
; // weak, from nsCycleCollector
1219 nsCString mNextEdgeName
;
1223 GCGraphBuilder(GCGraph
&aGraph
,
1224 nsCycleCollectionLanguageRuntime
**aRuntimes
);
1227 PRUint32
Count() const { return mPtrToNodeMap
.entryCount
; }
1230 PtrInfo
* AddNode(void *s
, nsCycleCollectionParticipant
*aParticipant
,
1233 PtrInfo
* AddNode(void *s
, nsCycleCollectionParticipant
*aParticipant
);
1234 PtrInfo
* AddNode(void *s
, nsCycleCollectionParticipant
*aParticipant
,
1237 return AddNode(s
, aParticipant
);
1240 void Traverse(PtrInfo
* aPtrInfo
);
1242 // nsCycleCollectionTraversalCallback methods.
1243 NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports
*root
);
1247 NS_IMETHOD_(void) DescribeNode(CCNodeType type
, nsrefcnt refCount
,
1248 size_t objSz
, const char *objName
);
1250 NS_IMETHOD_(void) DescribeNode(CCNodeType type
, nsrefcnt refCount
);
1252 NS_IMETHOD_(void) NoteRoot(PRUint32 langID
, void *child
,
1253 nsCycleCollectionParticipant
* participant
);
1254 NS_IMETHOD_(void) NoteXPCOMChild(nsISupports
*child
);
1255 NS_IMETHOD_(void) NoteNativeChild(void *child
,
1256 nsCycleCollectionParticipant
*participant
);
1257 NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID
, void *child
);
1259 NS_IMETHOD_(void) NoteNextEdgeName(const char* name
);
1263 GCGraphBuilder::GCGraphBuilder(GCGraph
&aGraph
,
1264 nsCycleCollectionLanguageRuntime
**aRuntimes
)
1265 : mNodeBuilder(aGraph
.mNodes
),
1266 mEdgeBuilder(aGraph
.mEdges
),
1267 mRuntimes(aRuntimes
)
1269 if (!PL_DHashTableInit(&mPtrToNodeMap
, &PtrNodeOps
, nsnull
,
1270 sizeof(PtrToNodeEntry
), 32768))
1271 mPtrToNodeMap
.ops
= nsnull
;
1274 GCGraphBuilder::~GCGraphBuilder()
1276 if (mPtrToNodeMap
.ops
)
1277 PL_DHashTableFinish(&mPtrToNodeMap
);
1281 GCGraphBuilder::AddNode(void *s
, nsCycleCollectionParticipant
*aParticipant
1282 IF_DEBUG_CC_PARAM(PRUint32 aLangID
)
1285 PtrToNodeEntry
*e
= static_cast<PtrToNodeEntry
*>(PL_DHashTableOperate(&mPtrToNodeMap
, s
, PL_DHASH_ADD
));
1289 result
= mNodeBuilder
.Add(s
, aParticipant
1290 IF_DEBUG_CC_PARAM(aLangID
)
1293 PL_DHashTableRawRemove(&mPtrToNodeMap
, e
);
1299 NS_ASSERTION(result
->mParticipant
== aParticipant
,
1300 "nsCycleCollectionParticipant shouldn't change!");
1306 GCGraphBuilder::Traverse(PtrInfo
* aPtrInfo
)
1311 if (!mCurrPi
->mParticipant
) {
1312 Fault("unknown pointer during walk", aPtrInfo
);
1317 mCurrPi
->mFirstChild
= mEdgeBuilder
.Mark();
1319 nsresult rv
= aPtrInfo
->mParticipant
->Traverse(aPtrInfo
->mPointer
, *this);
1320 if (NS_FAILED(rv
)) {
1321 Fault("script pointer traversal failed", aPtrInfo
);
1324 mCurrPi
->mLastChild
= mEdgeBuilder
.Mark();
1327 NS_IMETHODIMP_(void)
1328 GCGraphBuilder::NoteXPCOMRoot(nsISupports
*root
)
1330 root
= canonicalize(root
);
1332 "Don't add objects that don't participate in collection!");
1335 if (nsCycleCollector_shouldSuppress(root
))
1339 nsXPCOMCycleCollectionParticipant
*cp
;
1340 ToParticipant(root
, &cp
);
1342 NoteRoot(nsIProgrammingLanguage::CPLUSPLUS
, root
, cp
);
1346 NS_IMETHODIMP_(void)
1347 GCGraphBuilder::NoteRoot(PRUint32 langID
, void *root
,
1348 nsCycleCollectionParticipant
* participant
)
1350 NS_ASSERTION(root
, "Don't add a null root!");
1352 if (langID
> nsIProgrammingLanguage::MAX
|| !mRuntimes
[langID
]) {
1353 Fault("adding root for unregistered language", root
);
1357 AddNode(root
, participant
, langID
);
1360 NS_IMETHODIMP_(void)
1362 GCGraphBuilder::DescribeNode(CCNodeType type
, nsrefcnt refCount
,
1363 size_t objSz
, const char *objName
)
1365 GCGraphBuilder::DescribeNode(CCNodeType type
, nsrefcnt refCount
)
1369 mCurrPi
->mBytes
= objSz
;
1370 mCurrPi
->mName
= PL_strdup(objName
);
1373 if (type
== RefCounted
) {
1374 if (refCount
== 0 || refCount
== PR_UINT32_MAX
)
1375 Fault("zero or overflowing refcount", mCurrPi
);
1377 mCurrPi
->mRefCount
= refCount
;
1380 mCurrPi
->mRefCount
= type
== GCMarked
? PR_UINT32_MAX
: 0;
1383 sCollector
->mStats
.mVisitedNode
++;
1387 NS_IMETHODIMP_(void)
1388 GCGraphBuilder::NoteXPCOMChild(nsISupports
*child
)
1391 nsCString
edgeName(mNextEdgeName
);
1392 mNextEdgeName
.Truncate();
1394 if (!child
|| !(child
= canonicalize(child
)))
1398 if (nsCycleCollector_shouldSuppress(child
))
1402 nsXPCOMCycleCollectionParticipant
*cp
;
1403 ToParticipant(child
, &cp
);
1405 PtrInfo
*childPi
= AddNode(child
, cp
, nsIProgrammingLanguage::CPLUSPLUS
);
1408 mEdgeBuilder
.Add(childPi
);
1410 mCurrPi
->mEdgeNames
.AppendCString(edgeName
);
1412 ++childPi
->mInternalRefs
;
1416 NS_IMETHODIMP_(void)
1417 GCGraphBuilder::NoteNativeChild(void *child
,
1418 nsCycleCollectionParticipant
*participant
)
1421 nsCString
edgeName(mNextEdgeName
);
1422 mNextEdgeName
.Truncate();
1427 NS_ASSERTION(participant
, "Need a nsCycleCollectionParticipant!");
1429 PtrInfo
*childPi
= AddNode(child
, participant
, nsIProgrammingLanguage::CPLUSPLUS
);
1432 mEdgeBuilder
.Add(childPi
);
1434 mCurrPi
->mEdgeNames
.AppendCString(edgeName
);
1436 ++childPi
->mInternalRefs
;
1439 NS_IMETHODIMP_(void)
1440 GCGraphBuilder::NoteScriptChild(PRUint32 langID
, void *child
)
1443 nsCString
edgeName(mNextEdgeName
);
1444 mNextEdgeName
.Truncate();
1449 if (langID
> nsIProgrammingLanguage::MAX
) {
1450 Fault("traversing pointer for unknown language", child
);
1454 if (!mRuntimes
[langID
]) {
1455 NS_WARNING("Not collecting cycles involving objects for scripting "
1456 "languages that don't participate in cycle collection.");
1460 nsCycleCollectionParticipant
*cp
= mRuntimes
[langID
]->ToParticipant(child
);
1464 PtrInfo
*childPi
= AddNode(child
, cp
, langID
);
1467 mEdgeBuilder
.Add(childPi
);
1469 mCurrPi
->mEdgeNames
.AppendCString(edgeName
);
1471 ++childPi
->mInternalRefs
;
1475 NS_IMETHODIMP_(void)
1476 GCGraphBuilder::NoteNextEdgeName(const char* name
)
1478 mNextEdgeName
= name
;
1483 AddPurpleRoot(GCGraphBuilder
&builder
, nsISupports
*root
)
1485 root
= canonicalize(root
);
1487 "Don't add objects that don't participate in collection!");
1489 nsXPCOMCycleCollectionParticipant
*cp
;
1490 ToParticipant(root
, &cp
);
1492 PtrInfo
*pinfo
= builder
.AddNode(root
, cp
,
1493 nsIProgrammingLanguage::CPLUSPLUS
);
1498 cp
->UnmarkPurple(root
);
1504 nsCycleCollector::SelectPurple(GCGraphBuilder
&builder
)
1506 mPurpleBuf
.BumpGeneration();
1507 mPurpleBuf
.SelectAgedPointers(builder
);
1511 nsCycleCollector::MarkRoots(GCGraphBuilder
&builder
)
1513 mGraph
.mRootCount
= builder
.Count();
1515 // read the PtrInfo out of the graph that we are building
1516 NodePool::Enumerator
queue(mGraph
.mNodes
);
1517 while (!queue
.IsDone()) {
1518 PtrInfo
*pi
= queue
.GetNext();
1519 builder
.Traverse(pi
);
1524 ////////////////////////////////////////////////////////////////////////
1525 // Bacon & Rajan's |ScanRoots| routine.
1526 ////////////////////////////////////////////////////////////////////////
1529 struct ScanBlackWalker
: public GraphWalker
1531 ScanBlackWalker(PRUint32
&aWhiteNodeCount
) : mWhiteNodeCount(aWhiteNodeCount
)
1535 PRBool
ShouldVisitNode(PtrInfo
const *pi
)
1537 return pi
->mColor
!= black
;
1540 void VisitNode(PtrInfo
*pi
)
1542 if (pi
->mColor
== white
)
1546 sCollector
->mStats
.mSetColorBlack
++;
1550 PRUint32
&mWhiteNodeCount
;
1554 struct scanWalker
: public GraphWalker
1556 scanWalker(PRUint32
&aWhiteNodeCount
) : mWhiteNodeCount(aWhiteNodeCount
)
1560 PRBool
ShouldVisitNode(PtrInfo
const *pi
)
1562 return pi
->mColor
== grey
;
1565 void VisitNode(PtrInfo
*pi
)
1567 if (pi
->mInternalRefs
> pi
->mRefCount
&& pi
->mRefCount
> 0)
1568 Fault("traversed refs exceed refcount", pi
);
1570 if (pi
->mInternalRefs
== pi
->mRefCount
|| pi
->mRefCount
== 0) {
1574 sCollector
->mStats
.mSetColorWhite
++;
1577 ScanBlackWalker(mWhiteNodeCount
).Walk(pi
);
1578 NS_ASSERTION(pi
->mColor
== black
,
1579 "Why didn't ScanBlackWalker make pi black?");
1583 PRUint32
&mWhiteNodeCount
;
1587 nsCycleCollector::ScanRoots()
1589 mWhiteNodeCount
= 0;
1591 // On the assumption that most nodes will be black, it's
1592 // probably faster to use a GraphWalker than a
1593 // NodePool::Enumerator.
1594 scanWalker(mWhiteNodeCount
).WalkFromRoots(mGraph
);
1597 // Sanity check: scan should have colored all grey nodes black or
1598 // white. So we ensure we have no grey nodes at this point.
1599 NodePool::Enumerator
etor(mGraph
.mNodes
);
1600 while (!etor
.IsDone())
1602 PtrInfo
*pinfo
= etor
.GetNext();
1603 if (pinfo
->mColor
== grey
) {
1604 Fault("valid grey node after scanning", pinfo
);
1611 ////////////////////////////////////////////////////////////////////////
1612 // Bacon & Rajan's |CollectWhite| routine, somewhat modified.
1613 ////////////////////////////////////////////////////////////////////////
1616 nsCycleCollector::RootWhite()
1618 // Explanation of "somewhat modified": we have no way to collect the
1619 // set of whites "all at once", we have to ask each of them to drop
1620 // their outgoing links and assume this will cause the garbage cycle
1621 // to *mostly* self-destruct (except for the reference we continue
1624 // To do this "safely" we must make sure that the white nodes we're
1625 // operating on are stable for the duration of our operation. So we
1626 // make 3 sets of calls to language runtimes:
1628 // - Root(whites), which should pin the whites in memory.
1629 // - Unlink(whites), which drops outgoing links on each white.
1630 // - Unroot(whites), which returns the whites to normal GC.
1634 NS_ASSERTION(mWhiteNodes
->IsEmpty(),
1635 "FinishCollection wasn't called?");
1637 mWhiteNodes
->SetCapacity(mWhiteNodeCount
);
1639 NodePool::Enumerator
etor(mGraph
.mNodes
);
1640 while (!etor
.IsDone())
1642 PtrInfo
*pinfo
= etor
.GetNext();
1643 if (pinfo
->mColor
== white
&& mWhiteNodes
->AppendElement(pinfo
)) {
1644 rv
= pinfo
->mParticipant
->RootAndUnlinkJSObjects(pinfo
->mPointer
);
1645 if (NS_FAILED(rv
)) {
1646 Fault("Failed root call while unlinking", pinfo
);
1647 mWhiteNodes
->RemoveElementAt(mWhiteNodes
->Length() - 1);
1654 nsCycleCollector::CollectWhite()
1658 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
1659 struct _CrtMemState ms1
, ms2
;
1660 _CrtMemCheckpoint(&ms1
);
1663 PRUint32 i
, count
= mWhiteNodes
->Length();
1664 for (i
= 0; i
< count
; ++i
) {
1665 PtrInfo
*pinfo
= mWhiteNodes
->ElementAt(i
);
1666 rv
= pinfo
->mParticipant
->Unlink(pinfo
->mPointer
);
1667 if (NS_FAILED(rv
)) {
1668 Fault("Failed unlink call while unlinking", pinfo
);
1670 mStats
.mFailedUnlink
++;
1675 ++mStats
.mCollectedNode
;
1680 for (i
= 0; i
< count
; ++i
) {
1681 PtrInfo
*pinfo
= mWhiteNodes
->ElementAt(i
);
1682 rv
= pinfo
->mParticipant
->Unroot(pinfo
->mPointer
);
1684 Fault("Failed unroot call while unlinking", pinfo
);
1687 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
1688 _CrtMemCheckpoint(&ms2
);
1689 if (ms2
.lTotalCount
< ms1
.lTotalCount
)
1690 mStats
.mFreedBytes
+= (ms1
.lTotalCount
- ms2
.lTotalCount
);
1693 mCollectedObjects
+= count
;
1699 ////////////////////////////////////////////////////////////////////////
1700 // Memory-hooking stuff
1701 // When debugging wild pointers, it sometimes helps to hook malloc and
1702 // free. This stuff is disabled unless you set an environment variable.
1703 ////////////////////////////////////////////////////////////////////////
1705 static PRBool hookedMalloc
= PR_FALSE
;
1707 #if defined(__GLIBC__) && !defined(__UCLIBC__)
1710 static void* (*old_memalign_hook
)(size_t, size_t, const void *);
1711 static void* (*old_realloc_hook
)(void *, size_t, const void *);
1712 static void* (*old_malloc_hook
)(size_t, const void *);
1713 static void (*old_free_hook
)(void *, const void *);
1715 static void* my_memalign_hook(size_t, size_t, const void *);
1716 static void* my_realloc_hook(void *, size_t, const void *);
1717 static void* my_malloc_hook(size_t, const void *);
1718 static void my_free_hook(void *, const void *);
1723 __memalign_hook
= old_memalign_hook
;
1724 __realloc_hook
= old_realloc_hook
;
1725 __malloc_hook
= old_malloc_hook
;
1726 __free_hook
= old_free_hook
;
1732 // Glibc docs recommend re-saving old hooks on
1733 // return from recursive calls. Strangely when
1734 // we do this, we find ourselves in infinite
1737 // old_memalign_hook = __memalign_hook;
1738 // old_realloc_hook = __realloc_hook;
1739 // old_malloc_hook = __malloc_hook;
1740 // old_free_hook = __free_hook;
1746 __memalign_hook
= my_memalign_hook
;
1747 __realloc_hook
= my_realloc_hook
;
1748 __malloc_hook
= my_malloc_hook
;
1749 __free_hook
= my_free_hook
;
1753 my_realloc_hook(void *ptr
, size_t size
, const void *caller
)
1757 install_old_hooks();
1758 result
= realloc(ptr
, size
);
1762 sCollector
->Freed(ptr
);
1763 sCollector
->Allocated(result
, size
);
1766 install_new_hooks();
1773 my_memalign_hook(size_t size
, size_t alignment
, const void *caller
)
1777 install_old_hooks();
1778 result
= memalign(size
, alignment
);
1782 sCollector
->Allocated(result
, size
);
1784 install_new_hooks();
1791 my_free_hook (void *ptr
, const void *caller
)
1793 install_old_hooks();
1798 sCollector
->Freed(ptr
);
1800 install_new_hooks();
1805 my_malloc_hook (size_t size
, const void *caller
)
1809 install_old_hooks();
1810 result
= malloc (size
);
1814 sCollector
->Allocated(result
, size
);
1816 install_new_hooks();
1825 if (!hookedMalloc
) {
1827 install_new_hooks();
1828 hookedMalloc
= PR_TRUE
;
1832 #elif defined(WIN32)
1836 AllocHook(int allocType
, void *userData
, size_t size
, int
1837 blockType
, long requestNumber
, const unsigned char *filename
, int
1840 if (allocType
== _HOOK_FREE
)
1841 sCollector
->Freed(userData
);
1846 static void InitMemHook(void)
1848 if (!hookedMalloc
) {
1849 _CrtSetAllocHook (AllocHook
);
1850 hookedMalloc
= PR_TRUE
;
1853 #endif // __MINGW32__
1855 #elif 0 // defined(XP_MACOSX)
1857 #include <malloc/malloc.h>
1859 static void (*old_free
)(struct _malloc_zone_t
*zone
, void *ptr
);
1862 freehook(struct _malloc_zone_t
*zone
, void *ptr
)
1865 sCollector
->Freed(ptr
);
1866 old_free(zone
, ptr
);
1873 if (!hookedMalloc
) {
1874 malloc_zone_t
*default_zone
= malloc_default_zone();
1875 old_free
= default_zone
->free
;
1876 default_zone
->free
= freehook
;
1877 hookedMalloc
= PR_TRUE
;
1889 #endif // GLIBC / WIN32 / OSX
1892 ////////////////////////////////////////////////////////////////////////
1893 // Collector implementation
1894 ////////////////////////////////////////////////////////////////////////
1896 nsCycleCollector::nsCycleCollector() :
1897 mCollectionInProgress(PR_FALSE
),
1898 mScanInProgress(PR_FALSE
),
1899 mCollectedObjects(0),
1900 mWhiteNodes(nsnull
),
1903 mPurpleBuf(mParams
, mStats
),
1910 mExpectedGarbage
.Init();
1913 memset(mRuntimes
, 0, sizeof(mRuntimes
));
1914 mRuntimes
[nsIProgrammingLanguage::CPLUSPLUS
] = &mXPCOMRuntime
;
1918 nsCycleCollector::~nsCycleCollector()
1924 nsCycleCollector::RegisterRuntime(PRUint32 langID
,
1925 nsCycleCollectionLanguageRuntime
*rt
)
1927 if (mParams
.mDoNothing
)
1930 if (langID
> nsIProgrammingLanguage::MAX
)
1931 Fault("unknown language runtime in registration");
1933 if (mRuntimes
[langID
])
1934 Fault("multiple registrations of language runtime", rt
);
1936 mRuntimes
[langID
] = rt
;
1941 nsCycleCollector::ForgetRuntime(PRUint32 langID
)
1943 if (mParams
.mDoNothing
)
1946 if (langID
> nsIProgrammingLanguage::MAX
)
1947 Fault("unknown language runtime in deregistration");
1949 if (! mRuntimes
[langID
])
1950 Fault("forgetting non-registered language runtime");
1952 mRuntimes
[langID
] = nsnull
;
1958 WriteGraph(FILE *stream
, GCGraph
&graph
, const void *redPtr
)
1961 "digraph collection {\n"
1963 "node [fontname=fixed, fontsize=10, style=filled, shape=box]\n"
1966 NodePool::Enumerator
etor(graph
.mNodes
);
1967 while (!etor
.IsDone()) {
1968 PtrInfo
*pi
= etor
.GetNext();
1969 const void *p
= pi
->mPointer
;
1971 "n%p [label=\"%s\\n%p\\n",
1975 if (pi
->mRefCount
!= 0 && pi
->mRefCount
!= PR_UINT32_MAX
) {
1978 pi
->mInternalRefs
, pi
->mRefCount
);
1981 "\", fillcolor=%s, fontcolor=%s]\n",
1982 (redPtr
&& redPtr
== p
? "red" : (pi
->mColor
== black
? "black" : "white")),
1983 (pi
->mColor
== black
? "white" : "black"));
1984 for (EdgePool::Iterator child
= pi
->mFirstChild
,
1985 child_end
= pi
->mLastChild
;
1986 child
!= child_end
; ++child
) {
1987 fprintf(stream
, "n%p -> n%p\n", p
, (*child
)->mPointer
);
1991 fprintf(stream
, "\n}\n");
1996 nsCycleCollector::MaybeDrawGraphs()
1998 if (mParams
.mDrawGraphs
) {
1999 // We draw graphs only if there were any white nodes.
2000 PRBool anyWhites
= PR_FALSE
;
2001 NodePool::Enumerator
fwetor(mGraph
.mNodes
);
2002 while (!fwetor
.IsDone())
2004 PtrInfo
*pinfo
= fwetor
.GetNext();
2005 if (pinfo
->mColor
== white
) {
2006 anyWhites
= PR_TRUE
;
2012 // We can't just use _popen here because graphviz-for-windows
2013 // doesn't set up its stdin stream properly, sigh.
2016 stream
= fopen("c:\\cycle-graph.dot", "w+");
2018 stream
= popen("dotty -", "w");
2020 WriteGraph(stream
, mGraph
, nsnull
);
2023 // Even dotty doesn't work terribly well on windows, since
2024 // they execute lefty asynchronously. So we'll just run
2030 "\"load('dotty.lefty');"
2031 "dotty.simple('c:\\cycle-graph.dot');\"",
2033 unlink("c:\\cycle-graph.dot");
2042 public nsCycleCollectionTraversalCallback
2045 static char *sSuppressionList
;
2046 static PRBool sInitialized
;
2047 PRBool mSuppressThisNode
;
2053 PRBool
shouldSuppress(nsISupports
*s
)
2055 if (!sInitialized
) {
2056 sSuppressionList
= PR_GetEnv("XPCOM_CC_SUPPRESS");
2057 sInitialized
= PR_TRUE
;
2059 if (sSuppressionList
== nsnull
) {
2060 mSuppressThisNode
= PR_FALSE
;
2063 nsXPCOMCycleCollectionParticipant
*cp
;
2064 rv
= CallQueryInterface(s
, &cp
);
2065 if (NS_FAILED(rv
)) {
2066 Fault("checking suppression on wrong type of pointer", s
);
2069 cp
->Traverse(s
, *this);
2071 return mSuppressThisNode
;
2074 NS_IMETHOD_(void) DescribeNode(CCNodeType type
, nsrefcnt refCount
,
2075 size_t objSz
, const char *objName
)
2077 mSuppressThisNode
= (PL_strstr(sSuppressionList
, objName
) != nsnull
);
2080 NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports
*root
) {};
2081 NS_IMETHOD_(void) NoteRoot(PRUint32 langID
, void *root
,
2082 nsCycleCollectionParticipant
* participant
) {};
2083 NS_IMETHOD_(void) NoteXPCOMChild(nsISupports
*child
) {}
2084 NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID
, void *child
) {}
2085 NS_IMETHOD_(void) NoteNativeChild(void *child
,
2086 nsCycleCollectionParticipant
*participant
) {}
2087 NS_IMETHOD_(void) NoteNextEdgeName(const char* name
) {}
2090 char *Suppressor::sSuppressionList
= nsnull
;
2091 PRBool
Suppressor::sInitialized
= PR_FALSE
;
2094 nsCycleCollector_shouldSuppress(nsISupports
*s
)
2097 return supp
.shouldSuppress(s
);
2103 nsCycleCollector_isScanSafe(nsISupports
*s
)
2108 nsXPCOMCycleCollectionParticipant
*cp
;
2109 ToParticipant(s
, &cp
);
2111 return cp
!= nsnull
;
2116 nsCycleCollector::Suspect(nsISupports
*n
)
2118 // Re-entering ::Suspect during collection used to be a fault, but
2119 // we are canonicalizing nsISupports pointers using QI, so we will
2120 // see some spurious refcount traffic here.
2122 if (mScanInProgress
)
2125 NS_ASSERTION(nsCycleCollector_isScanSafe(n
),
2126 "suspected a non-scansafe pointer");
2127 NS_ASSERTION(NS_IsMainThread(), "trying to suspect from non-main thread");
2129 if (mParams
.mDoNothing
)
2133 mStats
.mSuspectNode
++;
2135 if (nsCycleCollector_shouldSuppress(n
))
2139 if (mParams
.mHookMalloc
)
2143 if (mParams
.mLogPointers
) {
2145 mPtrLog
= fopen("pointer_log", "w");
2146 fprintf(mPtrLog
, "S %p\n", static_cast<void*>(n
));
2157 nsCycleCollector::Forget(nsISupports
*n
)
2159 // Re-entering ::Forget during collection used to be a fault, but
2160 // we are canonicalizing nsISupports pointers using QI, so we will
2161 // see some spurious refcount traffic here.
2163 if (mScanInProgress
)
2166 NS_ASSERTION(NS_IsMainThread(), "trying to forget from non-main thread");
2168 if (mParams
.mDoNothing
)
2169 return PR_TRUE
; // it's as good as forgotten
2172 mStats
.mForgetNode
++;
2175 if (mParams
.mHookMalloc
)
2179 if (mParams
.mLogPointers
) {
2181 mPtrLog
= fopen("pointer_log", "w");
2182 fprintf(mPtrLog
, "F %p\n", static_cast<void*>(n
));
2186 mPurpleBuf
.Remove(n
);
2192 nsCycleCollector::Allocated(void *n
, size_t sz
)
2197 nsCycleCollector::Freed(void *n
)
2199 mStats
.mFreeCalls
++;
2202 // Ignore null pointers coming through
2206 if (mPurpleBuf
.Exists(n
)) {
2207 mStats
.mForgetNode
++;
2208 mStats
.mFreedWhilePurple
++;
2209 Fault("freed while purple", n
);
2210 mPurpleBuf
.Remove(n
);
2212 if (mParams
.mLogPointers
) {
2214 mPtrLog
= fopen("pointer_log", "w");
2215 fprintf(mPtrLog
, "R %p\n", n
);
2222 nsCycleCollector::Collect(PRUint32 aTryCollections
)
2224 #if defined(DEBUG_CC) && !defined(__MINGW32__)
2225 if (!mParams
.mDoNothing
&& mParams
.mHookMalloc
)
2229 // This can legitimately happen in a few cases. See bug 383651.
2230 if (mCollectionInProgress
)
2233 #ifdef COLLECT_TIME_DEBUG
2234 printf("cc: Starting nsCycleCollector::Collect(%d)\n", aTryCollections
);
2235 PRTime start
= PR_Now();
2238 mCollectionInProgress
= PR_TRUE
;
2240 nsCOMPtr
<nsIObserverService
> obs
=
2241 do_GetService("@mozilla.org/observer-service;1");
2243 obs
->NotifyObservers(nsnull
, "cycle-collector-begin", nsnull
);
2246 mFollowupCollection
= PR_FALSE
;
2247 mCollectedObjects
= 0;
2248 nsAutoTPtrArray
<PtrInfo
, 4000> whiteNodes
;
2249 mWhiteNodes
= &whiteNodes
;
2251 PRUint32 totalCollections
= 0;
2252 while (aTryCollections
> totalCollections
) {
2254 if (mRuntimes
[nsIProgrammingLanguage::JAVASCRIPT
]) {
2255 collected
= static_cast<nsCycleCollectionJSRuntime
*>
2256 (mRuntimes
[nsIProgrammingLanguage::JAVASCRIPT
])->Collect();
2259 collected
= BeginCollection() && FinishCollection();
2263 // We wait until after FinishCollection to check the white nodes because
2264 // some objects may outlive CollectWhite but then be freed by
2265 // FinishCycleCollection (like XPConnect's deferred release of native
2267 PRUint32 i
, count
= mWhiteNodes
->Length();
2268 for (i
= 0; i
< count
; ++i
) {
2269 PtrInfo
*pinfo
= mWhiteNodes
->ElementAt(i
);
2270 if (pinfo
->mLangID
== nsIProgrammingLanguage::CPLUSPLUS
&&
2271 mPurpleBuf
.Exists(pinfo
->mPointer
)) {
2272 printf("nsCycleCollector: %s object @%p is still alive after\n"
2273 " calling RootAndUnlinkJSObjects, Unlink, and Unroot on"
2274 " it! This probably\n"
2275 " means the Unlink implementation was insufficient.\n",
2276 pinfo
->mName
, pinfo
->mPointer
);
2280 mWhiteNodes
->Clear();
2289 mWhiteNodes
= nsnull
;
2291 mCollectionInProgress
= PR_FALSE
;
2294 // Now that the cycle collector has freed some memory, we can try to
2295 // force the C library to give back as much memory to the system as
2300 #ifdef COLLECT_TIME_DEBUG
2301 printf("cc: Collect() took %lldms\n",
2302 (PR_Now() - start
) / PR_USEC_PER_MSEC
);
2305 ExplainLiveExpectedGarbage();
2307 return mCollectedObjects
;
2311 nsCycleCollector::BeginCollection()
2313 if (mParams
.mDoNothing
)
2316 GCGraphBuilder
builder(mGraph
, mRuntimes
);
2318 #ifdef COLLECT_TIME_DEBUG
2319 PRTime now
= PR_Now();
2321 for (PRUint32 i
= 0; i
<= nsIProgrammingLanguage::MAX
; ++i
) {
2323 mRuntimes
[i
]->BeginCycleCollection(builder
);
2326 #ifdef COLLECT_TIME_DEBUG
2327 printf("cc: mRuntimes[*]->BeginCycleCollection() took %lldms\n",
2328 (PR_Now() - now
) / PR_USEC_PER_MSEC
);
2334 PRUint32 purpleStart
= builder
.Count();
2336 mScanInProgress
= PR_TRUE
;
2337 SelectPurple(builder
);
2339 PRUint32 purpleEnd
= builder
.Count();
2341 if (purpleStart
!= purpleEnd
) {
2343 if (mParams
.mHookMalloc
)
2346 if (mParams
.mLogPointers
&& !mPtrLog
)
2347 mPtrLog
= fopen("pointer_log", "w");
2350 NodePool::Enumerator
queue(mGraph
.mNodes
);
2351 while (i
++ < purpleStart
) {
2354 while (i
++ < purpleEnd
) {
2355 mStats
.mForgetNode
++;
2356 if (mParams
.mLogPointers
)
2357 fprintf(mPtrLog
, "F %p\n", queue
.GetNext()->mPointer
);
2362 #ifdef COLLECT_TIME_DEBUG
2363 printf("cc: SelectPurple() took %lldms\n",
2364 (PR_Now() - now
) / PR_USEC_PER_MSEC
);
2367 if (builder
.Count() > 0) {
2368 // The main Bacon & Rajan collection algorithm.
2370 #ifdef COLLECT_TIME_DEBUG
2376 #ifdef COLLECT_TIME_DEBUG
2378 PRTime then
= PR_Now();
2379 printf("cc: MarkRoots() took %lldms\n",
2380 (then
- now
) / PR_USEC_PER_MSEC
);
2387 #ifdef COLLECT_TIME_DEBUG
2388 printf("cc: ScanRoots() took %lldms\n",
2389 (PR_Now() - now
) / PR_USEC_PER_MSEC
);
2396 mScanInProgress
= PR_FALSE
;
2399 if (mFollowupCollection
&& purpleStart
!= purpleEnd
) {
2401 NodePool::Enumerator
queue(mGraph
.mNodes
);
2402 while (i
++ < purpleStart
) {
2405 while (i
++ < purpleEnd
) {
2406 PtrInfo
*pi
= queue
.GetNext();
2407 if (pi
->mColor
== white
) {
2408 printf("nsCycleCollector: a later shutdown collection collected the additional\n"
2410 " (which could be fixed by improving traversal)\n",
2411 pi
->mPointer
, pi
->mName
);
2417 #ifdef COLLECT_TIME_DEBUG
2422 #ifdef COLLECT_TIME_DEBUG
2423 printf("cc: RootWhite() took %lldms\n",
2424 (PR_Now() - now
) / PR_USEC_PER_MSEC
);
2428 mScanInProgress
= PR_FALSE
;
2435 nsCycleCollector::FinishCollection()
2437 #ifdef COLLECT_TIME_DEBUG
2438 PRTime now
= PR_Now();
2440 PRBool collected
= CollectWhite();
2442 #ifdef COLLECT_TIME_DEBUG
2443 printf("cc: CollectWhite() took %lldms\n",
2444 (PR_Now() - now
) / PR_USEC_PER_MSEC
);
2448 mStats
.mCollection
++;
2449 if (mParams
.mReportStats
)
2453 for (PRUint32 i
= 0; i
<= nsIProgrammingLanguage::MAX
; ++i
) {
2455 mRuntimes
[i
]->FinishCycleCollection();
2458 mFollowupCollection
= PR_TRUE
;
2464 nsCycleCollector::SuspectedCount()
2466 return mPurpleBuf
.Count();
2470 nsCycleCollector::Shutdown()
2472 // Here we want to run a final collection on everything we've seen
2473 // buffered, irrespective of age; then permanently disable
2474 // the collector because the program is shutting down.
2476 mParams
.mScanDelay
= 0;
2477 Collect(SHUTDOWN_COLLECTIONS(mParams
));
2480 GCGraphBuilder
builder(mGraph
, mRuntimes
);
2481 mScanInProgress
= PR_TRUE
;
2482 SelectPurple(builder
);
2483 mScanInProgress
= PR_FALSE
;
2484 if (builder
.Count() != 0) {
2485 printf("Might have been able to release more cycles if the cycle collector would "
2486 "run once more at shutdown.\n");
2490 mParams
.mDoNothing
= PR_TRUE
;
2495 static PLDHashOperator
2496 AddExpectedGarbage(nsVoidPtrHashKey
*p
, void *arg
)
2498 GCGraphBuilder
*builder
= static_cast<GCGraphBuilder
*>(arg
);
2500 static_cast<nsISupports
*>(const_cast<void*>(p
->GetKey()));
2501 builder
->NoteXPCOMRoot(root
);
2502 return PL_DHASH_NEXT
;
2505 struct SetSCCWalker
: public GraphWalker
2507 SetSCCWalker(PRUint32 aIndex
) : mIndex(aIndex
) {}
2508 PRBool
ShouldVisitNode(PtrInfo
const *pi
) { return pi
->mSCCIndex
== 0; }
2509 void VisitNode(PtrInfo
*pi
) { pi
->mSCCIndex
= mIndex
; }
2514 struct SetNonRootGreyWalker
: public GraphWalker
2516 PRBool
ShouldVisitNode(PtrInfo
const *pi
) { return pi
->mColor
== white
; }
2517 void VisitNode(PtrInfo
*pi
) { pi
->mColor
= grey
; }
2521 PrintPathToExpectedGarbage(PtrInfo
*pi
)
2523 printf(" An object expected to be garbage could be "
2524 "reached from it by the path:\n");
2525 for (PtrInfo
*path
= pi
, *prev
= nsnull
; prev
!= path
;
2527 path
= path
->mShortestPathToExpectedGarbage
) {
2529 nsCString
*edgeName
= prev
2530 ->mShortestPathToExpectedGarbageEdgeName
;
2532 edgeName
->IsEmpty() ? "<unknown edge>"
2535 printf(" %s %p\n", path
->mName
, path
->mPointer
);
2540 nsCycleCollector::ExplainLiveExpectedGarbage()
2542 if (mScanInProgress
|| mCollectionInProgress
)
2543 Fault("can't explain expected garbage during collection itself");
2545 if (mParams
.mDoNothing
) {
2546 printf("nsCycleCollector: not explaining expected garbage since\n"
2547 " cycle collection disabled\n");
2551 mCollectionInProgress
= PR_TRUE
;
2552 mScanInProgress
= PR_TRUE
;
2555 GCGraphBuilder
builder(mGraph
, mRuntimes
);
2557 for (PRUint32 i
= 0; i
<= nsIProgrammingLanguage::MAX
; ++i
) {
2559 mRuntimes
[i
]->BeginCycleCollection(builder
);
2562 // This might fail to explain expected garbage that's also in
2563 // the set of roots added by the runtimes (what used to be
2564 // called suspectCurrent), but that seems pretty unlikely.
2565 PRUint32 suspectCurrentCount
= builder
.Count();
2567 // Instead of adding roots from the purple buffer, we add them
2568 // from the list of nodes we were expected to collect.
2569 mExpectedGarbage
.EnumerateEntries(&AddExpectedGarbage
, &builder
);
2574 mScanInProgress
= PR_FALSE
;
2576 PRBool describeExtraRefcounts
= PR_FALSE
;
2577 PRBool findCycleRoots
= PR_FALSE
;
2579 NodePool::Enumerator
queue(mGraph
.mNodes
);
2581 while (!queue
.IsDone()) {
2582 PtrInfo
*pi
= queue
.GetNext();
2583 if (pi
->mColor
== white
) {
2584 findCycleRoots
= PR_TRUE
;
2587 if (pi
->mInternalRefs
!= pi
->mRefCount
&& i
>= suspectCurrentCount
) {
2588 describeExtraRefcounts
= PR_TRUE
;
2594 if ((describeExtraRefcounts
|| findCycleRoots
) &&
2595 CreateReversedEdges()) {
2596 // Note that the external references may have been external
2597 // to a different node in the cycle collection that just
2598 // happened, if that different node was purple and then
2601 // Use mSCCIndex temporarily to track whether we've reached
2602 // nodes in the breadth-first search.
2603 const PRUint32 INDEX_UNREACHED
= 0;
2604 const PRUint32 INDEX_REACHED
= 1;
2605 NodePool::Enumerator
etor_clear(mGraph
.mNodes
);
2606 while (!etor_clear
.IsDone()) {
2607 PtrInfo
*pi
= etor_clear
.GetNext();
2608 pi
->mSCCIndex
= INDEX_UNREACHED
;
2611 nsDeque queue
; // for breadth-first search
2612 NodePool::Enumerator
etor_roots(mGraph
.mNodes
);
2613 for (PRUint32 i
= 0; i
< mGraph
.mRootCount
; ++i
) {
2614 PtrInfo
*root_pi
= etor_roots
.GetNext();
2615 if (i
>= suspectCurrentCount
) {
2616 root_pi
->mSCCIndex
= INDEX_REACHED
;
2617 root_pi
->mShortestPathToExpectedGarbage
= root_pi
;
2618 queue
.Push(root_pi
);
2622 while (queue
.GetSize() > 0) {
2623 PtrInfo
*pi
= (PtrInfo
*)queue
.PopFront();
2624 for (ReversedEdge
*e
= pi
->mReversedEdges
; e
; e
= e
->mNext
) {
2625 if (e
->mTarget
->mSCCIndex
== INDEX_UNREACHED
) {
2626 e
->mTarget
->mSCCIndex
= INDEX_REACHED
;
2627 PtrInfo
*target
= e
->mTarget
;
2628 if (!target
->mShortestPathToExpectedGarbage
) {
2629 target
->mShortestPathToExpectedGarbage
= pi
;
2630 target
->mShortestPathToExpectedGarbageEdgeName
=
2637 if (pi
->mRefCount
== PR_UINT32_MAX
||
2638 (pi
->mInternalRefs
!= pi
->mRefCount
&& pi
->mRefCount
> 0)) {
2639 if (pi
->mRefCount
== PR_UINT32_MAX
) {
2640 printf("nsCycleCollector: %s %p was not collected due "
2642 " external references\n",
2643 pi
->mName
, pi
->mPointer
);
2646 printf("nsCycleCollector: %s %p was not collected due "
2648 " external references (%d total - %d known)\n",
2649 pi
->mName
, pi
->mPointer
,
2650 pi
->mRefCount
- pi
->mInternalRefs
,
2651 pi
->mRefCount
, pi
->mInternalRefs
);
2654 PrintPathToExpectedGarbage(pi
);
2656 if (pi
->mRefCount
== PR_UINT32_MAX
) {
2657 printf(" The known references to it were from:\n");
2660 printf(" The %d known references to it were from:\n",
2663 for (ReversedEdge
*e
= pi
->mReversedEdges
;
2666 e
->mTarget
->mName
, e
->mTarget
->mPointer
);
2667 if (!e
->mEdgeName
->IsEmpty()) {
2668 printf(" via %s", e
->mEdgeName
->get());
2672 mRuntimes
[pi
->mLangID
]->PrintAllReferencesTo(pi
->mPointer
);
2676 if (findCycleRoots
) {
2677 // NOTE: This code changes the white nodes that are not
2680 // Put the nodes in post-order traversal order from a
2681 // depth-first search.
2682 nsDeque DFSPostOrder
;
2685 // Use mSCCIndex temporarily to track the DFS numbering:
2686 const PRUint32 INDEX_UNREACHED
= 0;
2687 const PRUint32 INDEX_TRAVERSING
= 1;
2688 const PRUint32 INDEX_NUMBERED
= 2;
2690 NodePool::Enumerator
etor_clear(mGraph
.mNodes
);
2691 while (!etor_clear
.IsDone()) {
2692 PtrInfo
*pi
= etor_clear
.GetNext();
2693 pi
->mSCCIndex
= INDEX_UNREACHED
;
2698 NodePool::Enumerator
etor_roots(mGraph
.mNodes
);
2699 for (PRUint32 i
= 0; i
< mGraph
.mRootCount
; ++i
) {
2700 PtrInfo
*root_pi
= etor_roots
.GetNext();
2701 stack
.Push(root_pi
);
2704 while (stack
.GetSize() > 0) {
2705 PtrInfo
*pi
= (PtrInfo
*)stack
.Peek();
2706 if (pi
->mSCCIndex
== INDEX_UNREACHED
) {
2707 pi
->mSCCIndex
= INDEX_TRAVERSING
;
2708 for (EdgePool::Iterator child
= pi
->mFirstChild
,
2709 child_end
= pi
->mLastChild
;
2710 child
!= child_end
; ++child
) {
2715 // Somebody else might have numbered it already
2716 // (since this is depth-first, not breadth-first).
2717 // This happens if a node is pushed on the stack
2718 // a second time while it is on the stack in
2720 if (pi
->mSCCIndex
== INDEX_TRAVERSING
) {
2721 pi
->mSCCIndex
= INDEX_NUMBERED
;
2722 DFSPostOrder
.Push(pi
);
2728 // Put the nodes into strongly-connected components.
2730 NodePool::Enumerator
etor_clear(mGraph
.mNodes
);
2731 while (!etor_clear
.IsDone()) {
2732 PtrInfo
*pi
= etor_clear
.GetNext();
2736 PRUint32 currentSCC
= 1;
2738 while (DFSPostOrder
.GetSize() > 0) {
2739 SetSCCWalker(currentSCC
).Walk((PtrInfo
*)DFSPostOrder
.PopFront());
2744 // Mark any white nodes reachable from other components as
2747 NodePool::Enumerator
queue(mGraph
.mNodes
);
2748 while (!queue
.IsDone()) {
2749 PtrInfo
*pi
= queue
.GetNext();
2750 if (pi
->mColor
!= white
)
2752 for (EdgePool::Iterator child
= pi
->mFirstChild
,
2753 child_end
= pi
->mLastChild
;
2754 child
!= child_end
; ++child
) {
2755 if ((*child
)->mSCCIndex
!= pi
->mSCCIndex
) {
2756 SetNonRootGreyWalker().Walk(*child
);
2763 NodePool::Enumerator
queue(mGraph
.mNodes
);
2764 while (!queue
.IsDone()) {
2765 PtrInfo
*pi
= queue
.GetNext();
2766 if (pi
->mColor
== white
) {
2767 printf("nsCycleCollector: %s %p in component %d\n"
2768 " was not collected due to missing call to "
2769 "suspect, failure to unlink,\n"
2770 " or deficiency in traverse that causes "
2771 "cycles referenced only from other\n"
2772 " cycles to require multiple rounds of cycle "
2774 pi
->mName
, pi
->mPointer
, pi
->mSCCIndex
);
2775 if (pi
->mShortestPathToExpectedGarbage
)
2776 PrintPathToExpectedGarbage(pi
);
2782 DestroyReversedEdges();
2788 mCollectionInProgress
= PR_FALSE
;
2790 for (PRUint32 i
= 0; i
<= nsIProgrammingLanguage::MAX
; ++i
) {
2792 mRuntimes
[i
]->FinishCycleCollection();
2797 nsCycleCollector::CreateReversedEdges()
2799 // Count the edges in the graph.
2800 PRUint32 edgeCount
= 0;
2801 NodePool::Enumerator
countQueue(mGraph
.mNodes
);
2802 while (!countQueue
.IsDone()) {
2803 PtrInfo
*pi
= countQueue
.GetNext();
2804 for (EdgePool::Iterator e
= pi
->mFirstChild
, e_end
= pi
->mLastChild
;
2805 e
!= e_end
; ++e
, ++edgeCount
) {
2809 // Allocate a pool to hold all of the edges.
2810 mGraph
.mReversedEdges
= new ReversedEdge
[edgeCount
];
2811 if (mGraph
.mReversedEdges
== nsnull
) {
2812 NS_NOTREACHED("allocation failure creating reversed edges");
2816 // Fill in the reversed edges by scanning all forward edges.
2817 ReversedEdge
*current
= mGraph
.mReversedEdges
;
2818 NodePool::Enumerator
buildQueue(mGraph
.mNodes
);
2819 while (!buildQueue
.IsDone()) {
2820 PtrInfo
*pi
= buildQueue
.GetNext();
2822 for (EdgePool::Iterator e
= pi
->mFirstChild
, e_end
= pi
->mLastChild
;
2824 current
->mTarget
= pi
;
2825 current
->mEdgeName
= pi
->mEdgeNames
.CStringAt(i
);
2826 current
->mNext
= (*e
)->mReversedEdges
;
2827 (*e
)->mReversedEdges
= current
;
2832 NS_ASSERTION(current
- mGraph
.mReversedEdges
== ptrdiff_t(edgeCount
),
2838 nsCycleCollector::DestroyReversedEdges()
2840 NodePool::Enumerator
queue(mGraph
.mNodes
);
2841 while (!queue
.IsDone()) {
2842 PtrInfo
*pi
= queue
.GetNext();
2843 pi
->mReversedEdges
= nsnull
;
2846 delete mGraph
.mReversedEdges
;
2847 mGraph
.mReversedEdges
= nsnull
;
2851 nsCycleCollector::ShouldBeFreed(nsISupports
*n
)
2853 mExpectedGarbage
.PutEntry(n
);
2857 nsCycleCollector::WasFreed(nsISupports
*n
)
2859 mExpectedGarbage
.RemoveEntry(n
);
2863 ////////////////////////////////////////////////////////////////////////
2864 // Module public API (exported in nsCycleCollector.h)
2865 // Just functions that redirect into the singleton, once it's built.
2866 ////////////////////////////////////////////////////////////////////////
2869 nsCycleCollector_registerRuntime(PRUint32 langID
,
2870 nsCycleCollectionLanguageRuntime
*rt
)
2873 sCollector
->RegisterRuntime(langID
, rt
);
2878 nsCycleCollector_forgetRuntime(PRUint32 langID
)
2881 sCollector
->ForgetRuntime(langID
);
2886 NS_CycleCollectorSuspect(nsISupports
*n
)
2889 return sCollector
->Suspect(n
);
2895 NS_CycleCollectorForget(nsISupports
*n
)
2897 return sCollector
? sCollector
->Forget(n
) : PR_TRUE
;
2902 nsCycleCollector_collect()
2904 return sCollector
? sCollector
->Collect() : 0;
2908 nsCycleCollector_suspectedCount()
2910 return sCollector
? sCollector
->SuspectedCount() : 0;
2914 nsCycleCollector_beginCollection()
2916 return sCollector
? sCollector
->BeginCollection() : PR_FALSE
;
2920 nsCycleCollector_finishCollection()
2922 return sCollector
? sCollector
->FinishCollection() : PR_FALSE
;
2926 nsCycleCollector_startup()
2928 NS_ASSERTION(!sCollector
, "Forgot to call nsCycleCollector_shutdown?");
2930 sCollector
= new nsCycleCollector();
2931 return sCollector
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
2935 nsCycleCollector_shutdown()
2938 sCollector
->Shutdown();
2940 sCollector
= nsnull
;
2946 nsCycleCollector_DEBUG_shouldBeFreed(nsISupports
*n
)
2950 sCollector
->ShouldBeFreed(n
);
2955 nsCycleCollector_DEBUG_wasFreed(nsISupports
*n
)
2959 sCollector
->WasFreed(n
);