[plt-scheme] FFI - garbage collection

From: Yoav Goldberg (yoav.goldberg at gmail.com)
Date: Tue Oct 11 21:58:17 EDT 2005

> > Hi.
> > I am wrapping a dll using the v299 ffi.
> > As a part of my code, I have a c function that returns a pointer to
> > a structure:
> > (define-syntax defmshare
> >   (syntax-rules ()
> >      ((defmshare scheme-name libname type ...)
> >       (define scheme-name (get-ffi-obj libname mshare (type ...))))))
> >
> > (defmshare MidiNewEv* "MidiNewEv" _fun _short -> _TMidiEv-pointer)
> (Ugh, if you went all the way to a macro, why not make the macro add
> the _fun?  Otherwise it doesn't do much, and actually looks clearer
> with the parens.)
I find it more readable with the _fun and without the parens.. And the
macro is used just to save some typing..

> > I want to make sure the memory allocated by the "MidiNewEv" function
> > will be freed by GC,  and from what I understand, I need to use
> > "register-finalizaer", like so:
> > (defmshare MidiFreeEv "MidiFreeEv" _fun _TMidiEv-pointer -> _TMidiEv-pointer)
> > (define MidiNewEv ;; make sure chan/port/date are initialized to a default
> >   (lambda (type)
> >     (let ((ev (MidiNewEv* type)))
> >       (chan! ev 0)
> >       (port! ev 0)
> >       (date! ev 0)
> >       (register-finalizer ev MidiFreeEv)
> >       ev)))
> > I hope I got it right until now, let me know if I got something wrong.
> Looks fine.  You could do it by creating a new type that will do the
> setting and the finalizer.
Ok. I'll try it out soon.

> > But now comes the trickey part.
> > There is another C function:
> > (defmshare MidiSendIm "MidiSendIm" _fun _short _TMidiEv-pointer -> _void)
> > Which FREES any _TMidiEv-pointer passed to it.
> > As far as I understand this is a problem, since that pointer is
> > still registered with the finalizer for GC!
> > How can I fix this? Is there a way to "un-register" a finalizer, so
> > that I could do something like:
> > (define GoodMidiSendIm
> >    (lambda (n ev)
> >         (unregister-finalizer ev) (MidiSendIm n ev)))
> > ??
> There is no way to do this now (the implementation uses will
> executors).  But the problem here seems to be that you *don't* want
> automatic freeing of these objects because of the way they are
> managed.  Instead, you have to do the manual thing and knwo which
> TMidiEv's need to be freed.
Yes, but unlike C, it is very easy to create these things in Scheme
without any handle, and so there is no way to free them: (MidiNewEv
4).

> Perhaps the library is meant to be used in a way that you never create
> these objects unless you send them?  This means that another way to
> organize things is to have some Scheme objects that stand for these
> TMidiEv objects -- then, you do a wrapper for MidiSendIm that will get
> one of these Scheme objects, generate a real TMidiEv object,
> initialize it, and immediately send it over to MidiSendIm.
This is a possibility. Another option I though of after I sent the
mail was to use
the library function "MidiCopyEv", and wrap MidiSendIm so it will be
passed a copy instead of the original:
(let ((origMidiSendIm MidiSendIm))
   (set! MidiSendIm
       (lambda (chan event)
           (origMidiSendIm chan (MidiCopyEv event)))))

This seems very similar (and also quite the opposite?) to what you suggested.
Which do you think will be more efficiant?
Which do you think is nicer?

> One thing you have to be aware of, is that allocation using the Scheme
> `malloc' goes through the GC by default.  I don't think that it will
> be a good idea to pass such objects to a C library that will try to
> free them...
What is the Scheme "malloc", and what does it allocate?

Thanks
Yoav


Posted on the users mailing list.