[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