[gambit-list] How to pass a procedure to a nonnull-function?

Marc Feeley feeley at iro.umontreal.ca
Thu Nov 3 12:16:20 EDT 2011


On 2011-11-03, at 10:16 AM, Vok Vojwo wrote:

> 2011/11/3 Mikael <mikael.rcv at gmail.com>:
>> Yes. The external representation is effectively an int. Btw any closure can
>> be passed <-> the external world under the type representation scmobj which
>> is effectively an int also, look it up in the manual.
>> 
>> I'm not clear right now if scmobj:s are persistent to GC iterations though,
>> if you/anyone knows please let the ml know.
>> 
> 
> I think I got it:
> 
> ;; Local Variables:
> ;; compile-command: "gsc -exe ffi-hello-3.scm"
> ;; End:
> 
> (c-define (apply0 proc) (scheme-object) void "apply0" ""
>  (proc))
> 
> (c-declare #<<end-of-c-declare
> 
> void call_scheme (___SCMOBJ proc)
> {
>  apply0 (proc);
> }
> 
> end-of-c-declare
> )
> 
> (define call-scheme
>  (c-lambda (scheme-object) void "call_scheme"))
> 
> (define (proc)
>  (print "Hello, World!\n"))
> 
> (print (procedure? proc) "\n")
> 
> (call-scheme proc)
> 
> Now the C function call_scheme can call any Scheme procedure by using apply0.
> 
> With gc persistence you mean that the address of the scheme procedure
> might change because of a mark and compact gc? That would be a problem
> and will break the above code.

Yes that's the problem with that approach.

Another approach is to have multiple instances of the function and "allocate" them dynamically.  Check this message for that solution:

https://mercure.iro.umontreal.ca/pipermail/gambit-list/2009-January/002939.html

> The Gambit documentation distinguishes movable and nonmovable objects in:
> 
> http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php?title=Documentation:Procedure_gc-report-set!
> 
> But I can not find any description how to make an object non movable.

Objects are either allocated as "movable" or as "still" (non-movable).  Once allocated, you can't change their "movability".  On the other hand you can use (##still-copy obj) to make a still copy of obj.  The address of the copy will not change during its lifetime.

Like any other object, a still object will normally be reclaimed by the GC if it is not reachable from Scheme.  So if you pass the only reference to the copy to C, it will be reclaimed at the next GC.  On the other hand, you can force a still object to be considered live by the GC by incrementing its reference count:

(##still-obj-refcount-inc! obj)

When the C code is done with the object, it can decrement the reference count with the call:

___still_obj_refcount_dec(obj);

These things can be combined to safely pass closures to C.  The idea is to use a still wrapper around the closure:

(define (wrap proc)
  (##still-obj-refcount-inc! (##still-copy (vector proc))))

(define (install-callback proc)
  ((c-lambda (scheme-object) void "... ___arg1 should be used as the callback data ...")
   (wrap proc)))

(c-define (scheme-callback wrapped-proc arg) (scheme-object char-string) void "scheme_callback" ""
  ((vector-ref wrapped-proc 0) arg))

The actual C callback needs to pass the "callback data" (the wrapped closure) to scheme-callback by calling scheme_callback.

Marc



More information about the Gambit-list mailing list