Suppose I have some code written like so:
import Control.Exception (bracketOnError, finally, mask)
import Client (acquireB, releaseB, doStuff) -- theoretical
import MyStuff (preliminary, doMoreStuff) -- theoretical
clientAction = bracketOnError acquireB releaseB $ \b ->
doStuff b
return (b, releaseB b)
myAction = mask $ \restore ->
a <- preliminary
(thing, releaseThing) <- restore clientAction
doMoreStuff a thing `finally` releaseThing
I have to do some preliminary stuff, my client acquires b and then has to doStuff with b, and then I have to doMoreStuff with b. And b needs to be released, even if an asynchronous exception occurs. But I don't know how to release b, so my client tells me how. Written this way, we are both prepared to release b if an exception happens during "our" code.
My question is this: Is it ever possible for an async exception to cause releaseB not to be performed? Specifically, is there a gap between "my code" and "my client's code" where an async exception can squeeze in? Let me inline clientAction and bracketOnException to explain.
myAction = mask $ \restore -> do
a <- preliminary
(thing, releaseThing) <- restore $ mask $ \restore2 -> do
b <- acquireB
restore2 (doStuff b >> return (b, releaseB b))
`onException` releaseB b
doMoreStuff a thing `finally` releaseThing
The concern is this: is there a moment right here
... restore $ mask ...
Right when the client's mask is lifted, but before the end of my restore surrounding it, where an exception could sneak through?
Yes, in GHC, when the unmask occurs we eagerly check for blocked exceptions and raise them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With