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