WrapConc: Difference between revisions
(New safer forkIO functions) |
mNo edit summary |
||
Line 69: | Line 69: | ||
-- operations run but you are guranteed that the thread has started | -- operations run but you are guranteed that the thread has started | ||
-- and that the ThreadFlag will be set when it finishes. Your code | -- and that the ThreadFlag will be set when it finishes. Your code | ||
-- starts to | -- starts to execute in the scope of a 'block' function. | ||
-- | -- | ||
-- It is possible to interrupt the fork and to have still succeeded in | -- It is possible to interrupt the fork and to have still succeeded in | ||
Line 86: | Line 86: | ||
-- guaranteed that your operations run but you are guranteed that the | -- guaranteed that your operations run but you are guranteed that the | ||
-- thread has started and that the ThreadFlag will be set when it | -- thread has started and that the ThreadFlag will be set when it | ||
-- finishes. Your code starts to | -- finishes. Your code starts to execute in the scope of a 'block' | ||
-- function. | -- function. | ||
-- | -- | ||
Line 107: | Line 107: | ||
-- handler will run if your code received or raises an exception. And | -- handler will run if your code received or raises an exception. And | ||
-- the ThreadFlag will be set when the thread finishes. Your code | -- the ThreadFlag will be set when the thread finishes. Your code | ||
-- starts to | -- starts to execute in the scope of a 'block' function. | ||
-- | -- | ||
-- This is a stronger guarantee than (fork (catch ioA ioEA)) | -- This is a stronger guarantee than (fork (catch ioA ioEA)) | ||
Line 126: | Line 126: | ||
-- that your second command will be run even if your first command | -- that your second command will be run even if your first command | ||
-- receives or raises an exception. And the ThreadFlag will be set | -- receives or raises an exception. And the ThreadFlag will be set | ||
-- when the thread finishes. Your code starts to | -- when the thread finishes. Your code starts to execute in the | ||
-- scope of a 'block' function. | -- scope of a 'block' function. | ||
-- | -- |
Revision as of 11:51, 25 January 2007
-- By Chris Kuklewicz <haskell at list dot mightyreason dot com>
--
-- This WrapConc module provides modified version of forkIO which have
-- been combined with block from Control.Exception to provide stronger
-- guarantees as well as notification when a thread has finished.
module WrapConc(finally',bracket',fork,forkB,forkC,forkF
,ThreadFlag,isSetThreadFlag,waitThreadFlag,waitKillThread,abandonThread) where
import Prelude hiding (catch)
import Control.Concurrent(forkIO,ThreadId,killThread,yield)
import Control.Concurrent.MVar(MVar,newEmptyMVar,tryPutMVar,takeMVar,putMVar,isEmptyMVar,readMVar)
import Control.Exception(Exception,catch,block,throw)
import Control.Monad (liftM)
-- Some code copied from http://darcs.haskell.org/packages/base/Control/Exception.hs
-- This is like finally but leaves your code to execute in 'block'
-- instead of 'unblock'
finally' :: IO a -> IO b -> IO a
finally' a sequel = block $ do
r <- catch a (\e -> sequel >> throw e)
sequel
return r
-- This is like bracket but leaves your code to execute in 'block'
-- instead of 'unblock'
bracket' :: IO a -- ^ computation to run first (\"acquire resource\")
-> (a -> IO b) -- ^ computation to run last (\"release resource\")
-> (a -> IO c) -- ^ computation to run in-between
-> IO c -- returns the value from the in-between computation
bracket' before after thing = block $ do
a <- before
r <- catch (thing a) (\e -> after a >> throw e )
after a
return r
-- ThreadFlag is a limited newtype of an MVar
newtype ThreadFlag = ThreadFlag (MVar ())
-- Intially unset, the ThreadFlag will become set when the thread is finished
isSetThreadFlag :: ThreadFlag -> IO Bool
isSetThreadFlag (ThreadFlag m) = liftM not (isEmptyMVar m)
-- If unset then block until set. This will wait for the thread to have finished
waitThreadFlag :: ThreadFlag -> IO ()
waitThreadFlag (ThreadFlag m) = readMVar m
-- This does a background killThread and then waits for the passed
-- ThreadFlag to signal that it is done via the ThreadFlag
waitKillThread :: (ThreadFlag,ThreadId) -> IO ()
waitKillThread (flag,tid) = forkIO (killThread tid) >> yield >> waitThreadFlag flag
-- This does two things:
-- First it does a background killThread.
-- Second it immediately ensure the ThreadFlag is set
--
-- The main effect will be to release any thread waiting on (or that
-- will wait on) the ThreadFlag.
--
-- This should only be needed in highly unusual situations where
-- waiting for thread to properly exit and set the ThreadFlag is
-- impossible. It may be useful if the thread has hung or the
-- application needs to perform an "emergency shutdown".
abandonThread (ThreadFlag m,tid) = forkIO (killThread tid) >> tryPutMVar m ()
-- Safer forkIO. If this returns then you are not guaranteed that your
-- operations run but you are guranteed that the thread has started
-- and that the ThreadFlag will be set when it finishes. Your code
-- starts to execute in the scope of a 'block' function.
--
-- It is possible to interrupt the fork and to have still succeeded in
-- starting the thread.
fork :: IO a -> IO (ThreadFlag,ThreadId)
fork ioA = block $ do
a <- newEmptyMVar
b <- newEmptyMVar
tid <- forkIO (finally' (putMVar a () >> yield >> ioA)
(tryPutMVar b ()) >> return ())
yield
takeMVar a
return (ThreadFlag b, tid)
-- fork merged with bracket'. If this returns then you are not
-- guaranteed that your operations run but you are guranteed that the
-- thread has started and that the ThreadFlag will be set when it
-- finishes. Your code starts to execute in the scope of a 'block'
-- function.
--
-- The is equivalent to (fork (bracket' ioA ioAB ioAC))
--
-- It is possible to interrupt the forkB and to have still succeeded in
-- starting the thread.
forkB :: IO a -> (a -> IO b) -> (a -> IO c) -> IO (ThreadFlag,ThreadId)
forkB ioA ioAB ioAC = block $ do
a <- newEmptyMVar
b <- newEmptyMVar
tid <- forkIO (finally' (putMVar a () >> yield >> bracket' ioA ioAB ioAC)
(tryPutMVar b ()) >> return ())
yield
takeMVar a
return (ThreadFlag b, tid)
-- fork merged with catch. If this returns then you are not guarenteed
-- that your operation is running, but you are guranteed that the
-- handler will run if your code received or raises an exception. And
-- the ThreadFlag will be set when the thread finishes. Your code
-- starts to execute in the scope of a 'block' function.
--
-- This is a stronger guarantee than (fork (catch ioA ioEA))
--
-- It is possible to interrupt the forkC and to have still succeeded in
-- starting the thread.
forkC :: IO a -> (Exception -> IO a) -> IO (ThreadFlag,ThreadId)
forkC ioA ioEA = block $ do
a <- newEmptyMVar
b <- newEmptyMVar
tid <- forkIO (finally' (catch (putMVar a () >> yield >> ioA) ioEA)
(tryPutMVar b ()) >> return ())
yield
takeMVar a
return (ThreadFlag b, tid)
-- fork merged with finally'. If this returns then you are guaranteed
-- that your second command will be run even if your first command
-- receives or raises an exception. And the ThreadFlag will be set
-- when the thread finishes. Your code starts to execute in the
-- scope of a 'block' function.
--
-- This is a stronger guarantee than (fork (finally' ioA ioB))
--
-- It is possible to interrupt the forkF and to have still succeeded in
-- starting the thread.
forkF :: IO a -> IO b -> IO (ThreadFlag,ThreadId)
forkF ioA ioB = block $ do
a <- newEmptyMVar
b <- newEmptyMVar
tid <- forkIO (finally' (finally' (putMVar a () >> yield >> ioA) ioB)
(tryPutMVar b ()) >> return ())
yield
takeMVar a
return (ThreadFlag b, tid)