On Oct 31, 2013, at 8:08 AM, Vijay Mathew vijay.the.lisper@gmail.com wrote:
What is the right way to prevent a Scheme object referenced by a C object from being garbage collected?
For example, I have a structure:
struct Callback { ___SCMOBJ cbfn; };
where `cbfn' is a procedure defined in Scheme. I want it to be garbage collected only after the Callback object itself is destroyed.
There are a few ways to achieve this. One approach depends on the allocation kind for the Scheme object contained in cbfn.
If cbfn is permanently allocated (allocation kind = ___PERM), then you don't need to protect it. Functions with no free variables are allocated this way.
If cbfn is a still object (allocation kind = ___STILL), then you can increment its reference count with ___still_obj_refcount_inc(cbfn) when you store it in the structure, and then call ___still_obj_refcount_dec(cbfn) when the structure is destroyed.
If cbfn is a movable object (allocation kind = ___MOVABLE0/1/2), then you can't store the object in the structure because its address will change when there is a GC, and the GC isn't aware that a reference to the object is in a C structure, so it can't update it. Unfortunately, functions with free variables are allocated as movable objects.
A better approach, that does not depend on the allocation kind, is to wrap the object in a reference counted structure allocated with ___alloc_rc. The function ___alloc_rc can be used to allocate blocks of memory in the C heap, with the same API as "malloc", except that ___release_rc must be called to release the block instead of calling "free". There are two advantages to a reference counted structure, 1) they have a reference count managed with ___addref_rc and ___release_rc, and 2) a GC managed Scheme object can be attached to a reference counted structure (functions ___set_data_rc and ___data_rc).
Here is an example, assuming cbfn is a Scheme object:
void *callback = ___EXT(___alloc_rc)(0); if (callback == NULL) out_of_mem(); ___EXT(___set_data_rc)(callback, cbfn);
then, to get the value of cbfn (possibly updated by the GC) use:
cbfn = ___EXT(___data_rc)(callback);
and finally, to release the reference counted structure:
___EXT(___release_rc)(callback);
Gambit's FFI uses such reference counted structures when converting from the Scheme to C representation. So if you pass a Scheme string to a c-lambda expecting a C string, then the pointer received by the C function points to a reference counted structure containing the C string. The C function can prevent deallocation of the C string by calling ___addref_string (which simply calls ___addref_rc). This is useful when the C string must outlive the C function that received it (for example, if that string needs to be stored in a table for later use by the C code).
Marc