Code archives/Miscellaneous/Semiautomatic reference counting

This code has been declared by its author to be Public Domain code.

Download source code

Semiautomatic reference counting by Yasha2012
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