It strikes me that this is winding up highly controversial.
Assuming that, well, things might not go your way in terms of getting bracket changed, let's think a bit about what a more retrenched solution would look like.
This way we have a continuum of fixes and can try to find the right point in the continuum.
So let's put forth a couple more colors for the bikeshed:
Documentation Only
The issue is that users use bracket assuming things will be safer than they are. At the very least we need to clearly document the fact that this isn't the case!
We could document the pattern of using uninterruptibleMask_ yourself in handlers for safety and include examples of where it is required.
Combinators
If we want to go further, we could introduce:
uninterruptibleBracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
uninterruptibleBracket acquire release = bracket acquire (uninterruptibleMask_ . release)
uninterruptibleBracket_ :: IO a -> IO b -> IO c -> IO c
uninterruptibleBracket_ acquire release = bracket_ acquire (uninterruptibleMask_ release)
etc.
and then upgrade the documentation for `bracket` and the like with a big fat warning about how certain common sense examples using bracket should really be using uninterruptibleBracket.
Merijn's Proposal
We could go further and switch the default behavior of bracket to that of uninterruptibleBracket above. Then we get Merijn's proposal.
But if we do that we should probably consider adding an `interruptibleBracket` that matches the existing behavior with the same caveats we would want to put on `bracket`.
The use cases that folks have that center around weird RPC handling scenarios in the release handler seem to fit into this niche.
This redefines bracket, like Merijn would prefer, because it is a source of very very hard to track down resource bugs, and make the few who actually want to do complex code that relies on active asynchronous exception support in the handler switch combinators.
It also has the benefit that the name interruptibleBracket is easier to explain than uninterruptibleBracket, which only made release uninterruptible.
The kind of code that would be affected is the kind of code that would be very visibly affected, whereas the kind of code that is broken right now is scattered across the entire ecosystem and is just subtly wrong.
Personal Thoughts
I started writing this proposal with a continuum in mind, thinking I'd land somewhere in the middle.
Normally, I'd be disinclined to change semantics on a function with such widespread use! I very strongly sympathize with Greg's position here.
However, personally, I think the "Going Further" solution above is the right solution, which is effectively Merijn's proposal with the addition of interruptibleBracket and the like.
However, in this case the only code that really can rely upon this behavior is code that happens to know it won't kill the thread from outside until it is in the handler, but that they want to do a thing that will throw them asynchronous exceptions _within_ the handler, and well, that is a marginal enough use case that I don't have much a problem marginalizing it further by forcing its practitioners to use a more exotic combinator. If they are absolutely allergic to the new semantics you can always swap all the existing uses of bracket to interruptibleBracket, and the very kind of user who would need these semantics is the kind of user who is equipped to carry out this sort of change.
This effectively puts me at +1 with the caveat that I'd like to see interruptibleBracket added.
-Edward