[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