Question

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?

Was it helpful?

Solution

Yes, in GHC, when the unmask occurs we eagerly check for blocked exceptions and raise them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top