Missing primitive for unmasking uninterruptibility of async exceptions?
Motivation
I'm looking at a bug where async is called in an uninterruptibleMask, causing a deadlock as the withAsync cannot complete because the Async cannot be canceled due to spawning in the uninterruptibleMask state.
The local solution is to write:
asyncWithUnmask $ \unmask -> unmask $ do
mask $ \restore -> ...
However, this leaves us in a slightly vulnerable state - that unmask lambda parameter is provided as unsafeUnmask, which removes all masking and then throws the async exception.
Looking into the code, it doesn't appear that there's a primitive that goes from MaskedUninterruptible to MaskedInterruptible. We have unmaskAsyncException#, which seems to remove all masking. Indeed, it seems like the first thing it does is throw any async exceptions on the thread - so the unmask $ mask pattern is immediately vulnerable.
The issue is located here: https://github.com/iand675/hs-opentelemetry/pull/42
Proposal
-- | Removes the uninterruptibility of the masked action.
--
-- Has no effect if we are in an unmasked or interruptibly masked state.
-- Insert appropriate unsafety caveats here.
unsafeUnUninterruptibleMask :: IO a -> IO a
unsafeUnUninterruptibleMask (IO io) = IO (unsafeUnUninterruptibleMask# io)