Skip to content

Commit

Permalink
Added support for grandchild threads.
Browse files Browse the repository at this point in the history
  • Loading branch information
timthelion committed Aug 15, 2012
1 parent f5da12c commit c23e19c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 9 deletions.
35 changes: 35 additions & 0 deletions Test/GrandChildTest.lhs
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,35 @@
What happens if I create a tree of threads like:

ThreadMain
ChildThread
GrandChildThread

And ChildThread ends? Normally GrandChildThread dies. This module tests whether ThreadManager successfully flattens this thread hierarchy to:

ThreadMain
ChildThread
GrandChildThread

thus saving GrandChildThread in the case of ChildThread's death.

>import qualified Control.Concurrent.ThreadManager as TM
>import Control.Concurrent

>main :: IO ()
>main = do
> tm <- TM.make
> TM.fork tm (childThread tm)
> TM.waitForAll tm
> print "If you see this message, your grandChild has died and you should morn like the poorest of poppers."


>childThread :: TM.ThreadManager -> IO ()
>childThread tm = do
> TM.fork tm (grandChildThread 0)

> return ()

>grandChildThread :: Int -> IO ()
>grandChildThread count =
> print count >> threadDelay 1000 >> (grandChildThread $ count+1)

48 changes: 40 additions & 8 deletions src/Control/Concurrent/ThreadManager.hs
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -26,16 +26,38 @@ data ThreadStatus =
| Threw SomeException | Threw SomeException
deriving Show deriving Show


newtype ThreadManager = TM (MVar (M.Map ThreadId (MVar ThreadStatus))) data ThreadManager = TM (MVar (M.Map ThreadId (MVar ThreadStatus))) (MVar (IO ()))
deriving Eq deriving Eq


-- | Make a thread manager. -- | Make a thread manager. This should be run in a thread that won't die.
make :: IO ThreadManager make :: IO ThreadManager
make = TM `fmap` newMVar M.empty make = do
print "hiii"
listenerMVar <- newEmptyMVar
mvarMaps <- newMVar M.empty
tm <- return $ TM mvarMaps listenerMVar
forkIO $ listen tm
return tm

-- | This thread becomes the parent of all other threads.
listen :: ThreadManager -> IO ()
listen tm@(TM map listenerMVar) = do
command <- takeMVar listenerMVar
command
listen tm

runCommand :: ThreadManager -> IO a -> IO a
runCommand tm@(TM _ listenerMVar) command = do
resultMVar <- newEmptyMVar
putMVar listenerMVar (command >>= putMVar resultMVar)
takeMVar resultMVar


-- | Make a managed thread. Uses 'forkIO'. -- | Make a managed thread. Uses 'forkIO'.
fork :: ThreadManager -> IO () -> IO ThreadId fork :: ThreadManager -> IO () -> IO ThreadId
fork (TM tm) action = fork tm action = runCommand tm (fork' tm action)

fork' :: ThreadManager -> IO () -> IO ThreadId
fork' (TM tm _) action =
modifyMVar tm $ \m -> do modifyMVar tm $ \m -> do
state <- newEmptyMVar state <- newEmptyMVar
tid <- forkIO $ do tid <- forkIO $ do
Expand All @@ -45,11 +67,16 @@ fork (TM tm) action =


-- | Make the given number of managed threads. -- | Make the given number of managed threads.
forkn :: ThreadManager -> Int -> IO () -> IO [ThreadId] forkn :: ThreadManager -> Int -> IO () -> IO [ThreadId]
forkn tm n = replicateM n . fork tm forkn tm n action = runCommand tm (forkn' tm n action)

forkn' :: ThreadManager -> Int -> IO () -> IO [ThreadId]
forkn' tm n = replicateM n . fork tm


-- | Get the status of a managed thread. -- | Get the status of a managed thread.
getStatus :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus) getStatus :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus)
getStatus (TM tm) tid = getStatus tm tid = runCommand tm $ getStatus' tm tid
getStatus' :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus)
getStatus' (TM tm _) tid =
modifyMVar tm $ \m -> modifyMVar tm $ \m ->
case M.lookup tid m of case M.lookup tid m of
Nothing -> return (m, Nothing) Nothing -> return (m, Nothing)
Expand All @@ -61,7 +88,10 @@ getStatus (TM tm) tid =


-- | Block until a specific managed thread terminates. -- | Block until a specific managed thread terminates.
waitFor :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus) waitFor :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus)
waitFor (TM tm) tid = waitFor tm tid = runCommand tm $ waitFor' tm tid

waitFor' :: ThreadManager -> ThreadId -> IO (Maybe ThreadStatus)
waitFor' (TM tm _) tid =
join . modifyMVar tm $ \m -> join . modifyMVar tm $ \m ->
return $ return $
case M.updateLookupWithKey (\_ _ -> Nothing) tid m of case M.updateLookupWithKey (\_ _ -> Nothing) tid m of
Expand All @@ -70,7 +100,9 @@ waitFor (TM tm) tid =


-- | Block until all managed threads terminate. -- | Block until all managed threads terminate.
waitForAll :: ThreadManager -> IO () waitForAll :: ThreadManager -> IO ()
waitForAll (TM tm) = waitForAll tm = runCommand tm $ waitForAll' tm
waitForAll' :: ThreadManager -> IO ()
waitForAll' (TM tm _) =
modifyMVar tm elems >>= mapM_ takeMVar modifyMVar tm elems >>= mapM_ takeMVar
where where
elems m = return (M.empty, M.elems m) elems m = return (M.empty, M.elems m)
2 changes: 1 addition & 1 deletion threadmanager.cabal
Original file line number Original file line Diff line number Diff line change
@@ -1,5 +1,5 @@
name: threadmanager name: threadmanager
version: 0.1.4 version: 0.1.5


category: Concurrency category: Concurrency


Expand Down

0 comments on commit c23e19c

Please sign in to comment.