@@ -36,7 +36,7 @@ import Data.IORef (IORef, atomicModifyIORef,
36 modifyIORef, newIORef, readIORef,
36 modifyIORef, newIORef, readIORef,
37 writeIORef)
37 writeIORef)
38import Data.List (delete)
38import Data.List (delete)
39−import Data.Maybe (fromJust)
39+import Data.Maybe (fromJust, isJust)
40import Unsafe.Coerce (unsafeCoerce)
40import Unsafe.Coerce (unsafeCoerce)
42import Strands.AsyncT
42import Strands.AsyncT
@@ -88,52 +88,35 @@ runContext ctx = do
88-- on consumption rate.
88-- on consumption rate.
89processOneEvent :: MonadIO m
89processOneEvent :: MonadIO m
90 => ChildEvent a
90 => ChildEvent a
91− -> Either (TChan (ChildEvent a)) (IORef [a])
92 -> [ThreadId]
91 -> [ThreadId]
93 -> m ([ThreadId], Maybe SomeException)
92 -> m ([ThreadId], Maybe SomeException)
94−processOneEvent ev dest pending = do
93+processOneEvent (ChildDone tid e) pending = do
95− -- Collect results unless we have already encountered an exception.
97− ChildDone _ (Just e) -> handleException e
98− ChildResult (Left e) -> handleException e
99− ChildDone tid Nothing -> return (delete tid pending, Nothing)
100− ChildResult (Right r) -> do
102− Left chan -> liftIO $ atomically $ writeTChan chan ev
104− liftIO $ modifyIORef ref $ \rs -> unsafeCoerce r : rs
105− return (pending, Nothing)
108− handleException e = do
109− liftIO $ mapM_ killThread pending
94+ when (isJust e) $ liftIO $ mapM_ killThread pending
110− return (pending, Just e)
95+ return (delete tid pending, Nothing)
112drainChildren :: MonadIO m
97drainChildren :: MonadIO m
113− => Either (TChan (ChildEvent a)) (IORef [a])
114− -> TChan (ChildEvent a)
98+ => TChan (ChildEvent a)
115 -> [ThreadId]
99 -> [ThreadId]
116 -> m ([ThreadId], Maybe SomeException)
100 -> m ([ThreadId], Maybe SomeException)
117−drainChildren dest cchan pending =
101+drainChildren cchan pending =
118 case pending of
102 case pending of
119 [] -> return (pending, Nothing)
103 [] -> return (pending, Nothing)
121 ev <- liftIO $ atomically $ readTChan cchan
105 ev <- liftIO $ atomically $ readTChan cchan
122− (p, e) <- processOneEvent ev dest pending
106+ (p, e) <- processOneEvent ev pending
123− maybe (drainChildren dest cchan p) (const $ return (p, e)) e
107+ maybe (drainChildren cchan p) (const $ return (p, e)) e
125waitForChildren :: MonadIO m => Context -> m (Maybe SomeException)
109waitForChildren :: MonadIO m => Context -> m (Maybe SomeException)
126waitForChildren ctx = do
110waitForChildren ctx = do
127 let pendingRef = pendingThreads ctx
111 let pendingRef = pendingThreads ctx
128 pending <- liftIO $ readIORef pendingRef
112 pending <- liftIO $ readIORef pendingRef
129− (p, e) <- drainChildren (getCtxResultDest ctx) (childChannel ctx) pending
113+ (p, e) <- drainChildren (childChannel ctx) pending
130 liftIO $ writeIORef pendingRef p
114 liftIO $ writeIORef pendingRef p
133tryReclaimZombies :: (MonadIO m, MonadThrow m) => Context -> m ()
117tryReclaimZombies :: (MonadIO m, MonadThrow m) => Context -> m ()
134tryReclaimZombies ctx = do
118tryReclaimZombies ctx = do
135− let dest = getCtxResultDest ctx
136− cchan = childChannel ctx
119+ let cchan = childChannel ctx
137 pendingRef = pendingThreads ctx
120 pendingRef = pendingThreads ctx
139 pending <- liftIO $ readIORef pendingRef
122 pending <- liftIO $ readIORef pendingRef
@@ -144,7 +127,7 @@ tryReclaimZombies ctx = do
144 case mev of
127 case mev of
145 Nothing -> return ()
128 Nothing -> return ()
146 Just ev -> do
129 Just ev -> do
147− (p, e) <- processOneEvent ev dest pending
130+ (p, e) <- processOneEvent ev pending
148 liftIO $ writeIORef pendingRef p
131 liftIO $ writeIORef pendingRef p
149 maybe (return ()) throwM e
132 maybe (return ()) throwM e
150 tryReclaimZombies ctx
133 tryReclaimZombies ctx
@@ -153,13 +136,12 @@ waitForOneEvent :: (MonadIO m, MonadThrow m) => Context -> m ()
153waitForOneEvent ctx = do
136waitForOneEvent ctx = do
154 -- XXX assert pending must have at least one element
137 -- XXX assert pending must have at least one element
155 -- assert that the tid is found in our list
138 -- assert that the tid is found in our list
156− let dest = getCtxResultDest ctx
157− cchan = childChannel ctx
139+ let cchan = childChannel ctx
158 pendingRef = pendingThreads ctx
140 pendingRef = pendingThreads ctx
160 ev <- liftIO $ atomically $ readTChan cchan
142 ev <- liftIO $ atomically $ readTChan cchan
161 pending <- liftIO $ readIORef pendingRef
143 pending <- liftIO $ readIORef pendingRef
162− (p, e) <- processOneEvent ev dest pending
144+ (p, e) <- processOneEvent ev pending
163 liftIO $ writeIORef pendingRef p
145 liftIO $ writeIORef pendingRef p
164 maybe (return ()) throwM e
146 maybe (return ()) throwM e
@@ -226,7 +208,6 @@ forkContext context = do
226 { parentChannel = Just (childChannel ctx)
208 { parentChannel = Just (childChannel ctx)
227 , pendingThreads = pendingRef
209 , pendingThreads = pendingRef
228 , childChannel = chan
210 , childChannel = chan
229− , accumResults = Nothing
232 beforeExit ctx res = do
213 beforeExit ctx res = do
@@ -361,7 +342,6 @@ loopContextWith ioaction context = AsyncT $ do
361 { parentChannel = parentChannel curCtx
342 { parentChannel = parentChannel curCtx
362 , pendingThreads = pendingThreads curCtx
343 , pendingThreads = pendingThreads curCtx
363 , childChannel = childChannel curCtx
344 , childChannel = childChannel curCtx
364− , accumResults = accumResults curCtx
@@ -518,36 +498,17 @@ noTrans x = AsyncT $ x >>= return . Just
518-- Running the monad
498-- Running the monad
519------------------------------------------------------------------------------
499------------------------------------------------------------------------------
521−collectResult :: MonadIO m => a -> StateT Context m ()
524− case parentChannel ctx of
526− let ref = fromJust $ accumResults ctx
527− liftIO $ modifyIORef ref $ \rs -> unsafeCoerce r : rs
529− -- XXX can we pass the result directly to the root thread
530− -- instead of passing through all the parents? We can let the
531− -- parent go away and handle the ChildDone events as well in
533− liftIO $ atomically $ writeTChan chan
534− (ChildResult (Right (unsafeCoerce r)))
536−-- XXX pass a collector function and return a Traversable?
537−-- XXX Ideally it should be a non-empty list.
539-- | Run an 'AsyncT m' computation and collect the results generated by each
501-- | Run an 'AsyncT m' computation and collect the results generated by each
540-- thread of the computation in a list.
502-- thread of the computation in a list.
541waitAsync :: forall m a b. (MonadIO m, MonadCatch m)
503waitAsync :: forall m a b. (MonadIO m, MonadCatch m)
542− => (a -> AsyncT m a) -> AsyncT m a -> m [a]
504+ => (a -> AsyncT m a) -> AsyncT m a -> m ()
543waitAsync finalizer m = do
505waitAsync finalizer m = do
544 childChan <- liftIO $ atomically newTChan
506 childChan <- liftIO $ atomically newTChan
545 pendingRef <- liftIO $ newIORef []
507 pendingRef <- liftIO $ newIORef []
546− resultsRef <- liftIO $ newIORef []
547 credit <- liftIO $ newIORef maxBound
508 credit <- liftIO $ newIORef maxBound
549 let ctx = initContext (empty :: AsyncT m a) childChan pendingRef credit
510 let ctx = initContext (empty :: AsyncT m a) childChan pendingRef credit
550− finalizer resultsRef
511+ finalizer
552 r <- try $ runStateT (runAsyncT $ m >>= finalizer) ctx
513 r <- try $ runStateT (runAsyncT $ m >>= finalizer) ctx
@@ -559,25 +520,28 @@ waitAsync finalizer m = do
559 e <- waitForChildren ctx
520 e <- waitForChildren ctx
560 case e of
521 case e of
561 Just (exc :: SomeException) -> throwM exc
522 Just (exc :: SomeException) -> throwM exc
562− Nothing -> liftIO $ readIORef resultsRef
523+ Nothing -> return ()
525+-- TBD throttling of producer based on conumption rate.
564-- | Invoked to store the result of the computation in the context and finish
527-- | Invoked to store the result of the computation in the context and finish
565-- the computation when the computation is done
528-- the computation when the computation is done
566−gatherResult :: MonadIO m => a -> AsyncT m a
529+gatherResult :: MonadIO m => IORef [a] -> a -> AsyncT m a
567−gatherResult x = AsyncT $ do
530+gatherResult ref r = do
568− collectResult x
531+ liftIO $ atomicModifyIORef ref $ \rs -> (r : rs, rs)
569− return Nothing
532+ mzero
571-- | Run an 'AsyncT m' computation and collect the results generated by each
534-- | Run an 'AsyncT m' computation and collect the results generated by each
572-- thread of the computation in a list.
535-- thread of the computation in a list.
573gather :: forall m a. (MonadIO m, MonadCatch m)
536gather :: forall m a. (MonadIO m, MonadCatch m)
574 => AsyncT m a -> m [a]
537 => AsyncT m a -> m [a]
539+ resultsRef <- liftIO $ newIORef []
575−gather m = waitAsync gatherResult m
540+ waitAsync (gatherResult resultsRef) m
541+ liftIO $ readIORef resultsRef
577-- | Run an 'AsyncT m' computation, wait for it to finish and discard the
543-- | Run an 'AsyncT m' computation, wait for it to finish and discard the
578-- results.
544-- results.
579wait :: forall m a. (MonadIO m, MonadCatch m)
545wait :: forall m a. (MonadIO m, MonadCatch m)
580 => AsyncT m a -> m ()
546 => AsyncT m a -> m ()
582− _ <- waitAsync (const mzero) m
547+wait m = waitAsync (const mzero) m