Reputation: 24832
I'm currently using essentially the following code to start worker threads.
import Control.Monad (void)
import Control.Concurrent (forkIO)
import Control.Exception (finally)
import Data.IORef (newIORef, writeIORef, readIORef)
startWorker :: IO a -> IO (IO Bool)
startWorker work = do
running <- newIORef True
_ <- forkIO $ void work `finally` writeIORef running False
return $ readIORef running
I.e. start doing some work on the background and return an IO action that lets the caller poll if the worker thread is still running or if it has stopped for some reason.
How reliable is this method? Are there any situations where the thread would die without calling the finally
block (excluding, of course, situations where the whole process is killed)? Is there a better way to achieve the same functionality?
Upvotes: 6
Views: 550
Reputation: 2763
Related to Yuras' answer, there may be some issues with the initial masking state as it's explained in Mask and forkIO section of Simon Marlow's book. The bottom line, as he explains, is:
The rule of thumb is that any exception-handling function called as the first thing in a
forkIO
is better written usingforkFinally
. In particular, if you find yourself writingforkIO (x
finallyy)
, then writeforkFinally x (\_ -> y)
instead. Better still, use the Async API, which handles these details for you.
Upvotes: 5
Reputation: 13876
Async exception could fire after the thread is created, but before "void work `finally` writeIORef running False" is started. Use forkFinally to handle that.
I can imaging that there are other issues. I'd recommend async library. It handles all the complexity for you. Use poll to check Async
status.
Upvotes: 7