On Mon, 2006-01-09 at 11:33 +0100, Benjamin Franksen wrote:
On Monday 09 January 2006 10:03, Axel Simon wrote:
On Sun, 2006-01-08 at 14:51 +0000, Keean Schupke wrote:
My solution to this when developing a database library for my own use was to define the API in a bracket notation style, and only provide safe functions. The idea is that the function obtains the resource, calls a function passed as an argument, then frees the resource, so all resouces are guaranteed to be freed in the correct order... for example:
dbConnectWith :: DbName -> (DbHandle -> IO Result) -> Result dbConnectWith name workFn = do handle <- dbConnectTo name workFn handle `finally` dbDisconnect handle
In this way you avoid finalizers... and everthing is safe providing you only export the "with" style functions from the library... Here's an example from the library, the connect function:
I suppose you meant to write "result" rather than "Result". This style of functions is only safe if the user ensures that DbHandle is never returned as part of the result. You should have that in your documentation.
I wanted to mention this too, but you were quicker ;)
As far as I can tell, the only general solution is to use finalizers and, if you really need to enforce a sequence of finialization, touchForeignPtr.
Repeat: touchForeignPtr can NOT be used to enforce finalization order.
Ok, I re-read that thread. I agree. However, it is likely that the library you're using allows connections to be freed after the database is freed. If not, you can still all three methods I posted to get around the problem: Each connection needs to ref the database pointer and deref that pointer when the finalizer is run. That way, the database is never dead until all finalizers of all connections are run.
A practical issue with touchForeignPtr is that it cannot be conveniently called from another finalizer, since the latter live in C.
What do you mean "live in C"? Can't or shouldn't finalizers be written in Haskell, too?
Well, before ghc 6.0 and the "new FFI", finalizers where Haskell functions. Since this was tricky to implement, so finalizers are now C functions. This has other implications, e.g. you can't pass a closure of several pointers to the C function, the C finalizer will just get the one pointer it wraps. Axel.