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"
33 #include "function_attributes.h"
35 void DumpSimpleBackTrace(VMGlobals
*g
);
37 const int kMaxPoolSet
= 7;
38 const int kNumGCSizeClasses
= 28;
39 const int kFinalizerSet
= kNumGCSizeClasses
;
40 const int kNumGCSets
= kNumGCSizeClasses
+ 1;
41 const int kScanThreshold
= 256;
48 void Init(int inSizeClass
);
50 bool HasFree() { return mFree
!= &mBlack
; }
64 SlotRef(PyrObject
* inObj
, int32 inIndex
) : obj(inObj
), slotIndex(inIndex
) {}
72 static const int kLazyCollectThreshold
= 1024;
75 PyrGC(VMGlobals
*g
, AllocPool
*inPool
, PyrClass
*mainProcessClass
, long poolSize
);
77 MALLOC PyrObject
* New(size_t inNumBytes
, long inFlags
, long inFormat
, bool inCollect
);
78 MALLOC PyrObject
* NewFrame(size_t inNumBytes
, long inFlags
, long inFormat
, bool inAccount
);
80 MALLOC
static PyrObject
* NewPermanent(size_t inNumBytes
,
81 long inFlags
, long inFormat
);
83 MALLOC PyrObject
* NewFinalizer(ObjFuncPtr finalizeFunc
, PyrObject
*inObject
, bool inCollect
);
85 bool IsBlack(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mBlackColor
; }
86 bool IsWhite(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mWhiteColor
; }
87 bool IsGrey(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== mGreyColor
; }
88 static bool IsMarker(PyrObjectHdr
* inObj
) { return inObj
->gc_color
== obj_gcmarker
; }
89 bool IsFree(PyrObjectHdr
* inObj
) { return (!(IsMarker(inObj
) ||
90 inObj
->IsPermanent() ||
95 bool ObjIsBlack(PyrObjectHdr
* inObj
) { return IsBlack(inObj
); }
96 bool ObjIsGrey(PyrObjectHdr
* inObj
) { return IsGrey(inObj
); }
97 bool ObjIsFree(PyrObjectHdr
* inObj
) { return IsFree(inObj
); }
100 // general purpose write barriers:
101 void GCWrite(PyrObjectHdr
* inParent
, PyrSlot
* inSlot
)
103 if (IsBlack(inParent
) && IsObj(inSlot
) && IsWhite(slotRawObject(inSlot
))) {
104 ToGrey(slotRawObject(inSlot
));
107 void GCWrite(PyrObjectHdr
* inParent
, PyrObjectHdr
* inChild
)
109 if (IsBlack(inParent
) && IsWhite(inChild
)) {
113 // when you know the parent is black:
114 void GCWriteBlack(PyrSlot
* inSlot
)
117 if (IsWhite(slotRawObject(inSlot
))) {
118 ToGrey(slotRawObject(inSlot
));
122 void GCWriteBlack(PyrObjectHdr
* inChild
)
124 if (IsWhite(inChild
)) {
128 // when you know the child is white
129 void GCWriteNew(PyrObjectHdr
* inParent
, PyrObjectHdr
* inChild
)
131 if (IsBlack(inParent
)) {
136 // users should not call anything below.
139 void Collect(int32 inNumToScan
);
142 if (mUncollectedAllocations
> kLazyCollectThreshold
)
145 void FullCollection();
146 void ScanFinalizers();
147 GCSet
* GetGCSet(PyrObjectHdr
* inObj
);
148 void CompletePartialScan(PyrObject
*obj
);
150 inline void ToGrey(PyrObjectHdr
* inObj
);
151 inline void ToGrey2(PyrObjectHdr
* inObj
);
152 void ToBlack(PyrObjectHdr
* inObj
);
153 void ToWhite(PyrObjectHdr
*inObj
);
154 void Free(PyrObjectHdr
* inObj
);
157 int32
StackDepth() { return mVMGlobals
->sp
- mStack
->slots
+ 1; }
158 PyrObject
* Stack() { return mStack
; }
159 void SetStack(PyrObject
* inStack
) { mStack
= inStack
; }
165 bool BlackToWhiteCheck(PyrObject
*objA
);
166 bool SanityMarkObj(PyrObject
*objA
, PyrObject
*fromObj
, int level
);
167 bool SanityClearObj(PyrObject
*objA
, int level
);
170 void DumpSet(int set
);
172 void BecomePermanent(PyrObject
*inObject
);
173 void BecomeImmutable(PyrObject
*inObject
);
175 bool IsPartialScanObject(PyrObject
* inObject
) const { return inObject
== mPartialScanObj
; }
176 int32
GetPartialScanIndex() const { return mPartialScanSlot
; }
179 inline PyrObject
* Allocate(size_t inNumBytes
, int32 sizeclass
, bool inCollect
);
180 static void throwMemfailed(size_t inNumBytes
);
182 void ScanSlots(PyrSlot
*inSlots
, long inNumToScan
);
183 void SweepBigObjects();
184 void DoPartialScan(int32 inObjSize
);
189 void DLRemove(PyrObjectHdr
*obj
);
190 void DLInsertAfter(PyrObjectHdr
*after
, PyrObjectHdr
*obj
);
191 void DLInsertBefore(PyrObjectHdr
*before
, PyrObjectHdr
*obj
);
194 void Finalize(PyrObject
*obj
);
200 VMGlobals
*mVMGlobals
;
202 AdvancingAllocPool mNewPool
;
203 GCSet mSets
[kNumGCSets
];
204 PyrProcess
*mProcess
; // the root is the pyrprocess which contains this struct
206 PyrObject
*mPartialScanObj
;
209 int32 mPartialScanSlot
;
214 int32 mFlips
, mCollects
, mAllocTotal
, mScans
, mNumAllocs
, mStackScans
, mNumPartialScans
, mSlotsScanned
, mUncollectedAllocations
;
216 unsigned char mBlackColor
, mGreyColor
, mWhiteColor
, mFreeColor
;
221 inline void PyrGC::DLRemove(PyrObjectHdr
*obj
)
223 obj
->next
->prev
= obj
->prev
;
224 obj
->prev
->next
= obj
->next
;
227 inline void PyrGC::DLInsertAfter(PyrObjectHdr
*after
, PyrObjectHdr
*obj
)
229 obj
->next
= after
->next
;
231 after
->next
->prev
= obj
;
235 inline void PyrGC::DLInsertBefore(PyrObjectHdr
*before
, PyrObjectHdr
*obj
)
237 obj
->prev
= before
->prev
;
239 before
->prev
->next
= obj
;
243 inline GCSet
* PyrGC::GetGCSet(PyrObjectHdr
* inObj
)
245 return mSets
+ (inObj
->classptr
== class_finalizer
? kFinalizerSet
: inObj
->obj_sizeclass
);
248 inline void PyrGC::ToBlack(PyrObjectHdr
*obj
)
252 //post("ToBlack %d\n", mNumGrey);
257 GCSet
*gcs
= GetGCSet(obj
);
258 DLInsertAfter(&gcs
->mBlack
, obj
);
260 obj
->gc_color
= mBlackColor
;
263 inline void PyrGC::ToWhite(PyrObjectHdr
*obj
)
267 //post("ToWhite %d\n", mNumGrey);
272 GCSet
*gcs
= GetGCSet(obj
);
273 DLInsertAfter(&gcs
->mWhite
, obj
);
275 obj
->gc_color
= mWhiteColor
;
278 inline void PyrGC::Free(PyrObjectHdr
* obj
)
282 //post("ToWhite %d\n", mNumGrey);
287 GCSet
*gcs
= GetGCSet(obj
);
288 DLInsertBefore(gcs
->mFree
, obj
);
291 obj
->gc_color
= mFreeColor
;
295 inline void PyrGC::ToGrey(PyrObjectHdr
* obj
)
297 /* move obj from white to grey */
298 /* link around object */
301 /* link in new place */
302 DLInsertAfter(&mGrey
, obj
);
304 /* set grey list pointer to obj */
305 obj
->gc_color
= mGreyColor
;
307 mNumToScan
+= 1L << obj
->obj_sizeclass
;
310 inline void PyrGC::ToGrey2(PyrObjectHdr
* obj
)
312 /* move obj from white to grey */
313 /* link around object */
316 /* link in new place */
317 DLInsertAfter(&mGrey
, obj
);
319 /* set grey list pointer to obj */
320 obj
->gc_color
= mGreyColor
;
324 inline PyrObject
* PyrGC::Allocate(size_t inNumBytes
, int32 sizeclass
, bool inCollect
)
326 if (inCollect
&& mNumToScan
>= kScanThreshold
)
330 mUncollectedAllocations
= 0;
332 ++mUncollectedAllocations
;
335 GCSet
*gcs
= mSets
+ sizeclass
;
337 PyrObject
* obj
= (PyrObject
*)gcs
->mFree
;
338 if (!IsMarker(obj
)) {
340 gcs
->mFree
= obj
->next
;
341 assert(obj
->obj_sizeclass
== sizeclass
);
343 if (sizeclass
> kMaxPoolSet
) {
345 int32 allocSize
= sizeof(PyrObjectHdr
) + (sizeof(PyrSlot
) << sizeclass
);
346 obj
= (PyrObject
*)mPool
->Alloc(allocSize
);
348 int32 allocSize
= sizeof(PyrObjectHdr
) + (sizeof(PyrSlot
) << sizeclass
);
349 obj
= (PyrObject
*)mNewPool
.Alloc(allocSize
);
352 throwMemfailed(inNumBytes
);
353 DLInsertAfter(&gcs
->mWhite
, obj
);
354 obj
->obj_sizeclass
= sizeclass
;