On 2012-03-24, at 4:21 PM, Estevo wrote:
My plan, when I asked that question, was to wrap unmanaged objects in a C structure of the form
typedef struct {___SCMOBJ* parent; <actual_struct_type> actual_struct;} <actual_struct_wrapper>;
and add ___BEGIN_(C|S)FUN_*** (etc.) handlers that create the Scheme reference to the parent. This reference gets deleted in the child's finalizer.
But that looked scary to me. My current idea is to just map the C struct to a Scheme record with fields 'parent' and 'c-data'. I'll use conversion to treat the record (and not the c-data field alone) as the Scheme representation of the object, so people won't accidentally hold onto the c-data and lose the record and therefore the reference to the parent. So the struct finalizer does nothing again; the parent reference gets cleaned up by normal garbage collection of the Scheme record.
If there is nothing very obvious that I'm missing you can safely ignore this question. The latter option seems good enough to me. I wouldn't have bothered you with this if I had thought of that one in first place.
This reminds me that Gambit provides a memory allocation function, ___alloc_rc, for allocating C objects on the C heap which have a back-pointer to a Scheme object, and also a reference count. The API is:
void *___alloc_rc(unsigned long n); void ___release_rc(void *ptr); void ___addref_rc(void *ptr); void ___set_data_rc(void *ptr, ___SCMOBJ val); ___SCMOBJ ___data_rc(void *ptr);
___alloc_rc(n) allocates from the C heap a reference counted block of memory which is able to contain n bytes and returns a pointer to the first byte of that block. The block of memory also contains a slot of type ___SCMOBJ, the Scheme "data". The reference count is initially 1 and the data is #f. In terms of implementation, the data slot is stored immediately before the first byte of the block.
___release_rc(ptr) decrements the reference count and reclaims the block of memory when the reference count reaches 0. So ___alloc_rc and ___release_rc are drop-in replacements for malloc and free (but you must not mix ___alloc_rc with free!).
___addref_rc(ptr) increments the reference count.
___set_data_rc(ptr, val) sets the data slot to val. As long as the reference count is positive, the GC will consider the data slot to be a root (in other words the data will remain live and will not be reclaimed by the GC).
___data_rc(ptr) returns the data slot.
The C interface uses ___alloc_rc for all of the C structures that it allocates. In particular, when a Scheme string is passed to a C function, the Scheme string is copied to a block of memory allocated with ___alloc_rc. After returning from the C function, the C interface will execute ___release_rc on the pointer to the C string. Normally this will reclaim the C string. However, the C function can call ___addref_rc to prevent this ___release_rc from deallocating the C string. A subsequent call to ___release_rc (somewhere else in the logic of the program) will reclaim the C string.
I haven't analyzed your problem in detail, but perhaps the "data" pointer in these reference counted objects can solve your problem.
Marc