Every Haskell article about exceptions repeats simple pattern: Use bracket function to allocate/release resources and you will be safe.
I have a concern, because I tested its behavior and detected that the thread can get async exception while working inside release section.
import Control.Exception
import Control.Concurrent
main = do
tid <- forkIO myThread
threadDelay 100000
throwTo tid StackOverflow
threadDelay 1000000
myThread =
bracket
(putStrLn "NEW")
(\() -> threadDelay 500000 >> putStrLn "CLEAN")
(\() -> putStrLn "USE")
In the snippet above "CLEAN" is not printed due async exception - so resource leaks! How it can be claimed safe?
I don't know everything it must be a reason. To make it safe as I see it I need to wrap every cleaning inside mask. It looks clumsy.
To make it safe as I see it I need to wrap every cleaning inside mask.
Unfortunately, I think the answer is exactly that. If you have cleanup actions that must be run for resource safety, and you want to maintain resource safety with async exceptions, then you must use an uninterruptible mask during cleanup.
It’s similar to restricting yourself to signal-safe functions inside Unix/Linux signal handlers or exception-safe functions in C++ destructors, except the criterion here is “uninterruptible”, whether inherent to the functions you call, or by masking (maybe simpler to get right).
Alternatively, you can use the bracket
from the unliftio
package, which uses uninterruptible masking in its cleanup handler. (Unrelated to your question, this package provides the MonadUnliftIO
class for lifting IO
operations through stateless wrappers; it’s a restricted alternative to the more complex MonadBaseControl
from monad-control
, which handles stateful wrappers over any base monad.)
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