This definition has a serious problem: "after a" (in either the exception handling case, or the ordinary case) can include interruptible actions which abort the cleanup.
This means bracket does not in fact guarantee the cleanup occurs.
For example:
readMVar = bracket takeMVar putMVar -- If async exception occurs during putMVar, MVar is broken!
withFile .. = bracket (openFile ..) hClose -- Async exception during hClose leaks the file handle!
Interruptible actions during "before" are fine (as long as "before" handles them properly). Interruptible actions during "after" are virtually always a bug -- at best leaking a resource, and at worst breaking the program's invariants.
I propose changing all the cleanup handlers to run under uninterruptibleMask, specifically:
bracket, bracketOnError, bracket_, catch, catchJust, finally, handle, handleJust, onException
should all simply wrap their exception/cancellation handler with uninterruptibleMask.
The danger of a deadlock is minimal when compared with the virtually guaranteed buggy incorrect handling of async exceptions during cleanup.
--
Eyal