
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 */ } }
I think there is another way to solve this, which is to provide the finalizer still in haskell code, but export the haskell code using FFI, so that I can use it as a plain, normal finalizer. I'm still unsure about this.
Calling Haskell code from the garbage collector is essentially impossible to do efficiently and correctly. Don't even try it, your sanity is not worth saving 3 lines of C coding. Stefan