[gambit-list] FFI and scheme-object

Marc Feeley feeley at iro.umontreal.ca
Tue Apr 5 10:25:23 EDT 2011


On 2011-04-05, at 9:06 AM, Diogo F. S. Ramos wrote:

> Alex Queiroz <asandroq at gmail.com> writes:
> 
>> On Tue, Apr 5, 2011 at 2:23 AM, Diogo F. S. Ramos <diogofsr at gmail.com> wrote:
>>> 
>>> I was looking around the examples from the distribution and I found
>>> (##still-copy), which made my code work, but I don't know why, and I
>>> couldn't find any mention of it inside the manual.
>>> 
>> 
>>     Gambit uses a copying garbage collector, and therefore move
>> objects in memory. ##STILL-COPY copies the object to a memory location
>> from where it won't be moved again. So I guess a garbage collection is
>> happening after the call to MAKE_BOOK.
> 
> I see. Nice.
> 
> Is this the right way to do it?
> 
> I mean, whenever I create a scheme object that I want to return to C
> code, but I don't want to make a conversion to a C type like a struct
> because I only access its values using accessors, should I call
> (##still-copy) when returning it?

As explained by Alex one of the issues is that the garbage-collector can move objects around.  For portability reasons, Gambit's garbage-collector does not scan the C stack (and other C allocated structures) to update references to objects allocated from Scheme.  So as a general rule C code should not handle or store references to "movable" Scheme objects.  The Gambit memory allocator supports two allocation policies for objects: "movable" and "still".  As the name implies, objects which are movable can be moved to a different address by the GC and objects which are still will stay in the same place for their whole lifetime.  References to still objects in C code do not have to be updated.  Note that when the FFI converts a C value to a Scheme value it will allocate still Scheme objects (for example the Scheme strings received as parameters by your make-book function).

However, for the garbage-collector to reclaim an unused still object, it is necessary to know if the C code has one or more references to it.  That's why still objects also have a reference count, which count the number of references to them from the C code (the references from the Scheme code are managed by marking the objects reachable from the roots, and objects with a nonzero reference count are treated as roots).

So for your code to work properly, the Scheme object returned to C should be a "still" object with a reference count of 1.  You can use the following primitives to achieve this:

in Scheme code:
- (##still-copy obj) copies the object obj to a still object with a reference count of 0
- (##still-obj-refcount-inc! obj) increment the reference count of the still object obj
- (##still-obj-refcount-dec! obj) decrement the reference count of the still object obj

in C code:
- ___still_obj_refcount_inc(obj) increment the reference count of the still object obj
- ___still_obj_refcount_dec(obj) decrement the reference count of the still object obj

So I would write your code like this (untested):

;;;
(c-define (make-book title author) (char-string char-string) scheme-object "make_book" ""
  (##still-obj-refcount-inc! (##still-copy (list title author))))  ;; *****CHANGED*****

(c-define (get-title book) (scheme-object) char-string "get_title" ""
  (car book))

(c-define (get-author book) (scheme-object) char-string "get_author" ""
  (cadr book))

((c-lambda () void "f"))
;;;

And here is my C code (bar.c):
/**/
#define ___VERSION 406001

#include <gambit.h>

void f()
{
   ___SCMOBJ newbook = make_book("foo", "bar");

   get_title(newbook);

   ___still_obj_refcount_dec(newbook);      /*****ADDED*****/
}
/**/

A question you should ask yourself is wether it is necessary for your C code to handle Scheme objects.  I try to do it as little as possible to avoid such issues.  Another way to approach the problem is to have the constructors in C (allocating C structures on the C heap using malloc or Gambit's ___alloc_rc function) and accessing them through FFI bindings (i.e. you expose C functions to the Scheme code to allocate/access the C structures).  ___alloc_rc has the same API as malloc (i.e. ___alloc_rc(10) will allocate a block of length 10 bytes on the C heap), but it maintains a reference count and a "data" slot which can be used to store a reference to a Scheme object.  Here is the API:

- void *___alloc_rc(unsigned long n);             allocates n bytes, with rc=1 and data=#f
- void ___release_rc(void *ptr);                  decrements rc, if now 0 free the block
- void ___addref_rc(void *ptr);                   increment rc
- ___SCMOBJ ___data_rc(void *ptr);                fetch data slot of block
- void ___set_data_rc(void *ptr, ___SCMOBJ val);  assign val to data slot of block

Marc




More information about the Gambit-list mailing list