[gambit-list] Creating and returning scheme lists inside C

Marc Feeley feeley at iro.umontreal.ca
Fri Dec 30 22:36:55 EST 2005


On 30-Dec-05, at 9:44 PM, Christian wrote:

> Marc: can you give more info? Which parts of the Gambit source do  
> you recommend to try to understand first?
>
>>    ___S8 one, two, three;
>>   one = 1;
>>   two = 2;
>>   three = 3;
>>
>>   return
>>   ___CONS(one,
>>           ___CONS(two,
>>                   ___CONS(three,
>>                           ___NUL)));
>
> Note that you would also have to convert the ___S8 values to  
> ___SCMOBJ, using ___FIX() probably.

Some quick notes about allocating Scheme objects in C code...

Scheme objects can be allocated using one of 3 allocation policies:

   ___MOVABLE   the object's location can change as a result of  
garbage collection
   ___STILL     the object's location cannot change as a result of  
garbage collection
   ___PERM      the object's location cannot change and it is never  
reclaimed by the GC

The macro ___CONS(car,cdr) allocates movable pairs,  
___BEGIN_ALLOC_VECTOR(n) allocates movable vectors, etc.  Movable  
objects are allocated by incrementing the heap pointer (the local  
variable ___hp which is a copy of the hp field of the processor state  
structure).  Still pairs are allocated with ___make_pair 
(car,cdr,policy), still vectors are allocated with ___make_vector 
(length,init,policy), etc.  Still objects have a reference count,  
initialized to 1 at allocation time, which is taken into account by  
the garbage collector to infer liveness (in **addition** to the  
normal roots).  This makes it easy to manipulate still objects from C  
code because the garbage collector does not have to be aware of local  
C variables referencing still objects (as long as the reference count  
is > 0 the references will stay valid).

Although it is technically possible, it is a bad idea to allocate  
movable objects from C code because any allocation can cause a  
garbage collection (and the C stack is neither treated as a root or  
updated by the garbage collector).

So the simplest is for C code to only allocate still Scheme objects,  
and to decrement the reference count back to 0 when the C code  
returns the object to Scheme code.

Below is an example inspired from sample code in the Gambit manual.

Marc


(c-declare #<<end-c-declare

___SCMOBJ square_alist (int n)
{
   /* builds an association list of the squares of 0 to n-1 */

   ___SCMOBJ result = ___NUL; /* start with the empty list */
   int i = n;

   /* build the list starting at the tail */

   while (--i >= 0)
     {
       ___SCMOBJ elem;
       ___SCMOBJ new_result;

       /*
        * Invariant: result is either the empty list or a ___STILL pair
        * with reference count equal to 1.  This is important because
        * it is possible that ___make_pair will invoke the garbage
        * collector and we don't want the reference in result to become
        * invalid (which would be the case if result was a ___MOVABLE
        * pair or if it had a zero reference count).
        */

       elem = ___EXT(___make_pair) (___FIX(i), ___FIX(i*i), ___STILL);

       if (___FIXNUMP(elem))
         {
           ___EXT(___release_scmobj) (result); /* allow GC to reclaim  
result */
           return elem; /* fixnum indicates allocation failed */
         }

       /*
        * Note that elem will be a ___STILL object with reference count
        * equal to 1, so there is no risk that it will be reclaimed or
        * moved if the following call to ___make_pair invokes the
        * garbage collector.
        */

       new_result = ___EXT(___make_pair) (elem, result, ___STILL);

       /*
        * We can zero the reference count of elem and result (if not
        * the empty list) because the pair now references these objects
        * and the pair's reference count is > 0 (it can't be reclaimed
        * or moved by the garbage collector).
        */

       ___EXT(___release_scmobj) (elem);
       ___EXT(___release_scmobj) (result);

       result = new_result;

       if (___FIXNUMP(result))
         return result; /* fixnum indicates allocation failed */
     }

   /*
    * Note that result is either the empty list or a ___STILL pair with
    * a reference count equal to 1.  The reference count must be
    * decremented to 0 when it is handed back to the Scheme world, so
    * that it can be reclaimed when the Scheme world no longer
    * references it.
    */

   ___EXT(___release_scmobj) (result);

   return result;
}

end-c-declare
)

(define square-alist (c-lambda (int) scheme-object "square_alist"))

(pp (square-alist 10)) ; test it




More information about the Gambit-list mailing list