
Hi Michael,
I wholeheartedly agreed that timeout n $ atomicallyWithIO ... is important,
but that would generally work even if the finalizer ran masked wouldn't it?
Most actions that block are interruptible.
Put another way, I'd be interested to see how you could make serialize safe
without this. Something like this:
serialize :: [Operation d] -> DatabaseHandle d -> IO ()
serialize ops (DatabaseHandle _ h) =
withMVar h (\h -> forM_ ops $ B.hPut h . runPut . safePut)
`onException`
magicalFileRollback h
(ignoring the tedious implementation of magicalFileRollback) doesn't look
quite right as if you got an async exception in the tiny window between
onException exiting and serialize exiting then the STM transaction would
roll back but the file wouldn't. I'm not convinced I totally understand
async exceptions so if you (or anyone else) can point out why this works
I'd be very grateful.
Cheers,
David
On 31 July 2015 at 13:18, Michael Schröder
Hi David, I appreciate your interest in my thesis!
A finalizer which has non-atomic real-world effects needs to be quite
careful about undoing those effects when exceptions are thrown. [...] If some of those B.hPut calls succeed but then one fails (e.g. the disk is full) then the transaction will be rolled back, but the on-disk state will be left partially written.
Yes, you are absolutely right! The example application lacks some of the safeguards one would expect in a production-ready system. It was intended to be more of a demonstration of how easily one can in principle build a database-like system on top of STM using finalizers. It still requires some engineering effort to make it entirely safe and practical. I should have documented this better—or just gone the extra mile and actually made it safe!
Even if the finalizer did include exception handling to deal with this situation, what happens with asynchronous exceptions? Does the finalizer run with async exceptions masked?
The finalizer does not run with async exceptions masked and you're right that one needs to be careful about how to deal with side-effects & async exceptions & cleanup within the finalizer—just like with any kind of I/O, really. In the TX example code, serialize should probably use withMVarMasked instead of withMVar. But I don't think the finalizer should run with async exceptions masked by default. There are uses cases where you really do want the finalizer to be interruptible from another thread, e.g. if you want to be able to timeout the whole transaction (STM part + finalizer part, as in: timeout n $ atomicallyWithIO ...)
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe