Skip to content

unsafeUnmask unmasks even inside uninterruptibleMask

Control.Exception exports

allowInterrupt :: IO ()
allowInterrupt = unsafeUnmask $ return ()

with documentation:

When invoked inside mask, this function allows a blocked asynchronous exception to be raised, if one exists. It is equivalent to performing an interruptible operation, but does not involve any actual blocking. When called outside mask, or inside uninterruptibleMask, this function has no effect.

However, this is not actually true: unsafeUnmask unmasks exceptions even inside uninterruptibleUnmask, as the attached test demonstrates (the test uses a foreign call just to have something non-interruptible but still observable; in particular, doing a print is interruptible because it uses an MVar under the hood).

I think it is possible to define a better unsafeUnmask in user-land:

interruptible :: IO a -> IO a
interruptible act = do
  st <- getMaskingState
  case st of
    Unmasked              -> act
    MaskedInterruptible   -> unsafeUnmask act
    MaskedUninterruptible -> act

but it still seems to be that we should either (i) change the behaviour of unsafeUnmask, or (ii) provide a version of unsafeUnmask with the behaviour as described and then change allowInterrupt to use that new version of unsafeUnmask, or at the very least (iii) change the documentation.

(One question with the above definition of interruptible is what happens when we nest mask and uninterruptibleMask?)

Trac metadata
Trac field Value
Version 7.8.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Runtime System
Test case
Differential revisions
BlockedBy
Related
Blocking
CC simonmar
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information