
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