I'd like to propose a change in the behavior of Control.Exception to help guarantee cleanups are not accidentally lost.

For example, bracket is defined as:
bracket before after thing =
  mask $ \restore -> do
    a <- before
    r <- restore (thing a) `onException` after a
    _ <- after a
    return r
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