On 2011-04-05, at 9:06 AM, Diogo F. S. Ramos wrote:
Alex Queiroz asandroq@gmail.com writes:
On Tue, Apr 5, 2011 at 2:23 AM, Diogo F. S. Ramos diogofsr@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