2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 The garbage collector for SuperCollider.
23 Based on Wilson and Johnstone's real time collector and the Baker treadmill.
30 #include "PyrObject.h"
31 #include "VMGlobals.h"
32 #include "AdvancingAllocPool.h"
34 void DumpSimpleBackTrace(VMGlobals
*g
);
36 const int kMaxPoolSet
= 7;
37 const int kNumGCSizeClasses
= 28;
38 const int kFinalizerSet
= kNumGCSizeClasses
;
39 const int kNumGCSets
= kNumGCSizeClasses
+ 1;
40 const int kScanThreshold
= 256;
47 void Init(int inSizeClass
);
49 bool HasFree() { return mFree
!= &mBlack
; }
63 SlotRef(PyrObject
* inObj
, int32 inIndex
) : obj(inObj
), slotIndex(inIndex
) {}
71 static const int kLazyCollectThreshold
= 1024;
74 PyrGC(VMGlobals
*g
, AllocPool
*inPool
, PyrClass
*mainProcessClass
, long poolSize
);
76 PyrObject
* New(size_t inNumBytes
, long inFlags
, long inFormat
, bool inCollect
);
77 PyrObject
* NewFrame(size_t inNumBytes
, long inFlags
, long inFormat
, bool inAccount
);
79 static PyrObject
* NewPermanent(size_t inNumBytes
,
80 long inFlags
, long inFormat
);
82 PyrObject
* NewFinalizer(ObjFuncPtr finalizeFunc
, PyrObject
*inObject
, bool inCollect
);
84 bool IsBlack(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mBlackColor
; }
85 bool IsWhite(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mWhiteColor
; }
86 bool IsGrey(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mGreyColor
; }
87 static bool IsMarker(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== obj_gcmarker
; }
88 bool IsFree(PyrObjectHdr
* inObj
) { return (!(IsMarker(inObj
) ||
89 inObj
->IsPermanent() ||
94 bool ObjIsBlack(PyrObjectHdr
* inObj
) { return IsBlack(inObj
); }
95 bool ObjIsGrey(PyrObjectHdr
* inObj
) { return IsGrey(inObj
); }
96 bool ObjIsFree(PyrObjectHdr
* inObj
) { return IsFree(inObj
); }
99 // general purpose write barriers:
100 void GCWrite(PyrObjectHdr
* inParent
, PyrSlot
* inSlot
)
102 if (IsBlack(inParent
) && IsObj(inSlot
) && IsWhite(slotRawObject(inSlot
))) {
103 ToGrey(slotRawObject(inSlot
));
106 void GCWrite(PyrObjectHdr
* inParent
, PyrObjectHdr
* inChild
)
108 if (IsBlack(inParent
) && IsWhite(inChild
)) {
112 // when you know the parent is black:
113 void GCWriteBlack(PyrSlot
* inSlot
)
116 if (IsWhite(slotRawObject(inSlot
))) {
117 ToGrey(slotRawObject(inSlot
));
121 void GCWriteBlack(PyrObjectHdr
* inChild
)
123 if (IsWhite(inChild
)) {
127 // when you know the child is white
128 void GCWriteNew(PyrObjectHdr
* inParent
, PyrObjectHdr
* inChild
)
130 if (IsBlack(inParent
)) {
135 // users should not call anything below.
138 void Collect(int32 inNumToScan
);
141 if (mUncollectedAllocations
> kLazyCollectThreshold
)
144 void FullCollection();
145 void ScanFinalizers();
146 GCSet
* GetGCSet(PyrObjectHdr
* inObj
);
147 void CompletePartialScan(PyrObject
*obj
);
149 inline void ToGrey(PyrObjectHdr
* inObj
);
150 inline void ToGrey2(PyrObjectHdr
* inObj
);
151 void ToBlack(PyrObjectHdr
* inObj
);
152 void ToWhite(PyrObjectHdr
*inObj
);
153 void Free(PyrObjectHdr
* inObj
);
156 int32
StackDepth() { return mVMGlobals
->sp
- mStack
->slots
+ 1; }
157 PyrObject
* Stack() { return mStack
; }
158 void SetStack(PyrObject
* inStack
) { mStack
= inStack
; }
164 bool BlackToWhiteCheck(PyrObject
*objA
);
165 bool SanityMarkObj(PyrObject
*objA
, PyrObject
*fromObj
, int level
);
166 bool SanityClearObj(PyrObject
*objA
, int level
);
169 void DumpSet(int set
);
171 void BecomePermanent(PyrObject
*inObject
);
172 void BecomeImmutable(PyrObject
*inObject
);
174 bool IsPartialScanObject(PyrObject
* inObject
) const { return inObject
== mPartialScanObj
; }
175 int32
GetPartialScanIndex() const { return mPartialScanSlot
; }
178 inline PyrObject
* Allocate(size_t inNumBytes
, int32 sizeclass
, bool inCollect
);
179 static void throwMemfailed(size_t inNumBytes
);
181 void ScanSlots(PyrSlot
*inSlots
, long inNumToScan
);
182 void SweepBigObjects();
183 void DoPartialScan(int32 inObjSize
);
188 void DLRemove(PyrObjectHdr
*obj
);
189 void DLInsertAfter(PyrObjectHdr
*after
, PyrObjectHdr
*obj
);
190 void DLInsertBefore(PyrObjectHdr
*before
, PyrObjectHdr
*obj
);
193 void Finalize(PyrObject
*obj
);
199 VMGlobals
*mVMGlobals
;
201 AdvancingAllocPool mNewPool
;
202 GCSet mSets
[kNumGCSets
];
203 PyrProcess
*mProcess
; // the root is the pyrprocess which contains this struct
205 PyrObject
*mPartialScanObj
;
208 int32 mPartialScanSlot
;
213 int32 mFlips
, mCollects
, mAllocTotal
, mScans
, mNumAllocs
, mStackScans
, mNumPartialScans
, mSlotsScanned
, mUncollectedAllocations
;
215 unsigned char mBlackColor
, mGreyColor
, mWhiteColor
, mFreeColor
;
220 inline void PyrGC::DLRemove(PyrObjectHdr
*obj
)
222 obj
->next
->prev
= obj
->prev
;
223 obj
->prev
->next
= obj
->next
;
226 inline void PyrGC::DLInsertAfter(PyrObjectHdr
*after
, PyrObjectHdr
*obj
)
228 obj
->next
= after
->next
;
230 after
->next
->prev
= obj
;
234 inline void PyrGC::DLInsertBefore(PyrObjectHdr
*before
, PyrObjectHdr
*obj
)
236 obj
->prev
= before
->prev
;
238 before
->prev
->next
= obj
;
242 inline GCSet
* PyrGC::GetGCSet(PyrObjectHdr
* inObj
)
244 return mSets
+ (inObj
->classptr
== class_finalizer
? kFinalizerSet
: inObj
->obj_sizeclass
);
247 inline void PyrGC::ToBlack(PyrObjectHdr
*obj
)
251 //post("ToBlack %d\n", mNumGrey);
256 GCSet
*gcs
= GetGCSet(obj
);
257 DLInsertAfter(&gcs
->mBlack
, obj
);
259 obj
->gc_color
= mBlackColor
;
262 inline void PyrGC::ToWhite(PyrObjectHdr
*obj
)
266 //post("ToWhite %d\n", mNumGrey);
271 GCSet
*gcs
= GetGCSet(obj
);
272 DLInsertAfter(&gcs
->mWhite
, obj
);
274 obj
->gc_color
= mWhiteColor
;
277 inline void PyrGC::Free(PyrObjectHdr
* obj
)
281 //post("ToWhite %d\n", mNumGrey);
286 GCSet
*gcs
= GetGCSet(obj
);
287 DLInsertBefore(gcs
->mFree
, obj
);
290 obj
->gc_color
= mFreeColor
;
294 inline void PyrGC::ToGrey(PyrObjectHdr
* obj
)
296 /* move obj from white to grey */
297 /* link around object */
300 /* link in new place */
301 DLInsertAfter(&mGrey
, obj
);
303 /* set grey list pointer to obj */
304 obj
->gc_color
= mGreyColor
;
306 mNumToScan
+= 1L << obj
->obj_sizeclass
;
309 inline void PyrGC::ToGrey2(PyrObjectHdr
* obj
)
311 /* move obj from white to grey */
312 /* link around object */
315 /* link in new place */
316 DLInsertAfter(&mGrey
, obj
);
318 /* set grey list pointer to obj */
319 obj
->gc_color
= mGreyColor
;
323 inline PyrObject
* PyrGC::Allocate(size_t inNumBytes
, int32 sizeclass
, bool inCollect
)
325 if (inCollect
&& mNumToScan
>= kScanThreshold
)
329 mUncollectedAllocations
= 0;
331 ++mUncollectedAllocations
;
334 GCSet
*gcs
= mSets
+ sizeclass
;
336 PyrObject
* obj
= (PyrObject
*)gcs
->mFree
;
337 if (!IsMarker(obj
)) {
339 gcs
->mFree
= obj
->next
;
340 assert(obj
->obj_sizeclass
== sizeclass
);
342 if (sizeclass
> kMaxPoolSet
) {
344 int32 allocSize
= sizeof(PyrObjectHdr
) + (sizeof(PyrSlot
) << sizeclass
);
345 obj
= (PyrObject
*)mPool
->Alloc(allocSize
);
347 int32 allocSize
= sizeof(PyrObjectHdr
) + (sizeof(PyrSlot
) << sizeclass
);
348 obj
= (PyrObject
*)mNewPool
.Alloc(allocSize
);
351 throwMemfailed(inNumBytes
);
352 DLInsertAfter(&gcs
->mWhite
, obj
);
353 obj
->obj_sizeclass
= sizeclass
;