[Haskell-cafe] Haskell FFI and finalizers

Maxime Henrion mux at FreeBSD.org
Wed Oct 3 18:55:41 EDT 2007


Stefan O'Rear wrote:
> On Wed, Oct 03, 2007 at 05:57:58PM +0200, Maxime Henrion wrote:
> > I have recently developed a small set of bindings for a C library, and
> > encountered a problem that I think could be interesting to others.
> > 
> > My problem was that the C function I was writing bindings to expects to
> > be passed a FILE *.  So, I had basically two possibles routes to take:
> 
> > That's the easy part.  Now my problem was that I had to find a way to
> > automatically close this FILE * when it isn't used anymore, in order not
> > to leak FILE structures (and thus fds, etc).  A finalizer is typically
> > what I need, but unfortunately, a finalizer has a very strict shape:
> > 
> > type FinalizerPtr a = FunPtr (Ptr a -> IO ())
> > 
> > That is, a finalizer can only be a pointer to a foreign function, and
> > the foreign function itself needs a quite specific shape.
> > 
> > And then I discovered Foreign.Concurrent, which allows one to associate
> > a plain Haskell IO action to a pointer.  The 'Foreign.Concurrent' name
> > is a bit misleading to me; it seems this module is named so because it
> > needs concurrency itself, rather than providing stuff for concurrency.
> 
> NOOO!  Foreign.Concurrent, as its name implies, works by forking
> threads, and it should be avoided at almost any cost.  The correct
> solution is:
> 
> void close_file_finalizer(FILE *file) {
>     if (fclose(file) < 0) {
>         /* do something sensible here */
>     }
> }

That wouldn't work; my problem is that this finalizer for closing the
FILE * needs to be called when another pointer gets garbage collected.
This is because I'm opening the file in order to pass to some function
which creates an objet and returns it to me.

To parody the situation:

struct foo *foo_new(FILE *);
void foo_destroy(struct foo *);

When writing the binding for foo_new(), I need to open a file with
fopen() to pass it the FILE *.  Then I get a struct foo * that I can
easily associate the the foo_destroy() finalizer.  However, when
finalizing the struct foo * object, I want to also close the FILE *
handle.

If I write a small C function for doing the finalizer myself, I still
wouldn't get passed the FILE * to close, only the struct foo * pointer
which is of no use.

Thanks,
Maxime


More information about the Haskell-Cafe mailing list