Code archives/Miscellaneous/Semiautomatic reference counting
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
This short and relatively simple snippet implements Objective-C 1.0 style memory management in Blitz3D. It requires the free FastPointer DLL by Mikhail Vostrikov. This style of memory management is based on "reference counting". It's a manual version of the same concept behind what powers the BlitzMax garbage collector. There are three main elements to it: 1) When an object is assigned to an ownership slot (put in a variable of known extent, or given to another object), it is retained. This increments its reference count. (As with the BlitzMax garbage collector, be aware of the circular-reference bug: in this implementation, you can actually make circular references, but you are expected to know which object is the "owner" and which is subordinate to it; the latter shouldn't retain its "owner".) 2) When an object leaves an ownership slot - its variable goes out of scope or is overwritten, its owning object is deleted - it is released. This decrements its reference count. If the reference count goes below 1, it is deleted. (Objects have a starting reference count of zero, so you can choose whether to retain or autorelease them.) 3) For temporary objects, remembering to do this is often a bit too much hassle. An object can instead be autoreleased, making it owned by an "autorelease pool". When the pool is cleared, all objects owned by it are released (so they can still have more formal owners), and the pool itself is deleted. Pools live on a global stack - in this case just a B3D typelist - so you can create and autorelease a bunch of temporaries in a function with some fast inner loop, and clear the whole pool after the function returns. Unfortunately for B3D-ers, this sort of memory management is heavily reliant on polymorphism: in order to be useful, autorelease pools must contain objects of more than one type, and call the right free functions on them when done. Implementing polymorphism in B3D is certainly possible, but it requires a lot of Select dispatch structures with Object/Handle, and for the user to modify the code of the release function when they define new types, which is a) a pain and b) a potential source of bugs. So in this implementation I have gone for the function-pointer approach instead: every RC object stores the free-function required, and that function is called on it when it is to be deleted. The included example shows how to set up a type to use reference counting. Just copy the function pointer line exactly and it will work fine (uncomment the DebugLog lines to see notifications when things happen). Notice that: -- a reference counted type must have a RefCounted field: initialize this using NewRefCounted, with the destructor function pointer, and the TypePointer of your object -- Retain, Release and AutoRelease are technically called on this field -- your type's Free function does not need to clear the RefCounted field, leave that to the library -- the type must have a dedicated destructor function whose pointer we can take and store -- Having a dedicated constructor is good practice for wrapping all this up (and maybe adding an automatic AutoRelease command for small types that are always used as temporaries) This implementation ought to work OK (it relies on nasty hacks, as all FastPointer code does, but B3D's implementation is pretty stable by now), but it might give some peace of mind to replace it with a "pure" version using Object/Handle and a Select dispatcher once the program is completed (plus it's one less DLL dependency). Implementing that should be an easy exercise for the interested reader, especially once they've understood this system fully. | |||||
; Retain/release/autorelease style memory management ; Requires FastPointer Type RefCounted Field rc, free, obj End Type Type AutoReleasePool Field objs, count, capacity End Type Const AUTORELEASE_POOL_START = 1024, RELEASABLE_OFFSET = 20 Function Retain(this.RefCounted) this\rc = this\rc + 1 End Function Function Release(this.RefCounted) DebugLog "Called Release on 0x" + Hex(this\obj) + " with refcount " + Hex(this\rc) this\rc = this\rc - 1 If this\rc < 1 Then CallFunctionVarInt this\free, this\obj : Delete this End Function Function AutoRelease(this.RefCounted) Local p.AutoReleasePool = Last AutoReleasePool If p\count = p\capacity Then p\capacity = p\capacity * 2 : ResizeBank p\objs, p\capacity * 4 PokeInt p\objs, p\count * 4, TypePointer(this) - RELEASABLE_OFFSET DebugLog "Autoreleased 0x" + Hex(this\obj) p\count = p\count + 1 : Retain this End Function Function NewRefCounted.RefCounted(free, obj) Local rc.RefCounted = New RefCounted rc\free = free : rc\obj = obj - RELEASABLE_OFFSET Return rc End Function Function NewAutoReleasePool.AutoReleasePool(capacity = AUTORELEASE_POOL_START) Local p.AutoReleasePool = New AutoReleasePool p\objs = CreateBank(capacity * 4) p\capacity = capacity Return p End Function Function ClearAutoReleasePool(p.AutoReleasePool) Local r.RefCounted, rPtr = FunctionPointer() : Goto skip : Release r .skip Local o : For o = 0 To p\count - 1 CallFunctionVarInt rPtr, PeekInt(p\objs, o * 4) Next FreeBank p\objs : Delete p End Function ;============================================================================== ; Example ;============================================================================== Type Foo Field rc.RefCounted Field a, b#, c$ End Type Function NewFoo.Foo(a_, b_#, c_$) Local f.Foo = New Foo f\a = a_ : f\b = b_ : f\c = c_ Local freePtr = FunctionPointer() : Goto skip : FreeFoo f .skip f\rc = NewRefCounted(freePtr, TypePointer(f)) DebugLog "Created new Foo at 0x" + Hex(TypePointer(f)) Return f End Function Function FreeFoo(this.Foo) DebugLog "Ran FreeFoo on 0x" + Hex(TypePointer(this)) ;... Delete this End Function Graphics 800, 400, 32, 6 Local p.AutoReleasePool = NewAutoReleasePool() Local i : For i = 1 To 10 Local f.Foo = NewFoo(1, 2.0, "three") AutoRelease f\rc If i > 4 Then Retain f\rc Next ClearAutoReleasePool p WaitKey End ;~IDEal Editor Parameters: ;~C#Blitz3D |
Comments
None.
Code Archives Forum