[gambit-list] References between 'foreign' objects.
Marc Feeley
feeley at iro.umontreal.ca
Sat Mar 24 17:26:20 EDT 2012
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
More information about the Gambit-list
mailing list