[plt-scheme] ffi and register-finalizer

From: Eli Barzilay (eli at barzilay.org)
Date: Fri May 20 13:18:27 EDT 2005

On May 20, Michael Reynolds wrote:
> For me it does not do this, the destructors are not called.  The
> printf runs and the pointer can be used but the destructor is not
> called automatically. I was wondering if anyone has more experience
> with this.  Is it supposed to garbage collect your c pointers? If
> not is there a way to make it do that? Or is there some other
> error(s) in what I am trying to do?

Your code seems fine.  I used the following C code:

| #include <stdlib.h>
| void* make_foo() {
|   void *p = malloc(100);
|   printf("allocating foo %d\n", p);
|   return p;
| }
| void free_foo(void *p) {
|   printf("de-allocating foo %d\n", p);
|   free(p);
| }

and the following Scheme code which is based on your code:

| > (define-cpointer-type _foo #f #f
|     (lambda (ptr)
|       (if ptr
|         (begin (register-finalizer ptr free_foo)
|                (printf "Registering ptr\n")
|                ptr)
|         (error '_foo "got a NULL pointer"))))
| > (define free_foo
|     (get-ffi-obj "free_foo" "x.so" (_fun _foo -> _void)))
| > (define make_foo
|     (get-ffi-obj "make_foo" "x.so" (_fun -> _foo)))

Using this, things seem to be running fine:

| > (define a (list (make_foo)))
| allocating foo 136969432
| Registering ptr
| > (set! a (cons (make_foo) a))
| allocating foo 136969536
| Registering ptr
| > (set! a (cons (make_foo) a))
| allocating foo 136969640
| Registering ptr
| > (set! a (cons (make_foo) a))
| allocating foo 136969744
| Registering ptr
| > (set! a '())
| > (collect-garbage)
| de-allocating foo 136969432
| de-allocating foo 136969744
| de-allocating foo 136969536
| de-allocating foo 136969640

But make sure that you read the documentation -- this is delicate

* If you allocate things in Scheme (or in some extension code that
  uses scheme_malloc) then there is no need to use `free'.

* The destructor can be any function which can be registered with any
  Scheme object, in fact the whole thing is not tightly related to the
  foreign interface and is provided for convenience.

* An implication of this is that you want to register a finalizer only
  once with every unique pointer.  Otherwise, if there are two Scheme
  pointer objects with the same value, then the destructor will be
  called twice.

* Two standard solutions are
  (a) have a normal _foo pointer type with no finalizer and an
      additional _foo* with it, use _foo* only for foreign functions
      that create such objects
  (b) use a single _foo type with no finalizers, and for functions
      that construct such value, add wrapper code that will do the
      registration.  Like this:
        (_fun ... -> (p : _foo)
                  -> (begin (register-finalizer p free_foo) p))

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!

Posted on the users mailing list.