1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE StandaloneDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7-- Module : Asyncly.Threads
8-- Copyright : (c) 2017 Harendra Kumar
11-- Maintainer : harendra.kumar@gmail.com
12-- Stability : experimental
15module Asyncly.RunAsync
27import Control.Applicative (Alternative (..))
28import Control.Concurrent.STM (atomically, newTChan)
29import Control.Monad (liftM)
30import Control.Monad.Catch (MonadThrow)
31import Control.Monad.IO.Class (MonadIO (..))
32import Control.Monad.Trans.Class (MonadTrans (lift))
33import Control.Monad.State (StateT(..), runStateT)
34import Data.Monoid ((<>))
35import Data.IORef (IORef, newIORef, readIORef)
37import Control.Monad.Trans.Recorder (MonadRecorder(..), RecorderT,
38 Recording, blank, runRecorderT)
41-- This transformer runs AsyncT under a state to manage the threads.
42-- Separating the state from the pure ListT transformer is cleaner but it
43-- results in 2x performance degradation. At some point if that performance is
44-- really needed we can combine the two.
47newtype AsynclyT m a = AsynclyT { runAsynclyT :: AsyncT (StateT Context m) a }
49deriving instance Monad m => Functor (AsynclyT m)
50deriving instance Monad m => Applicative (AsynclyT m)
51deriving instance Monad m => Alternative (AsynclyT m)
52deriving instance Monad m => Monad (AsynclyT m)
53deriving instance MonadIO m => MonadIO (AsynclyT m)
54deriving instance MonadThrow m => MonadThrow (AsynclyT m)
55instance MonadTrans (AsynclyT) where
56 lift mx = AsynclyT $ AsyncT $ \c _ k -> lift mx >>= (\a -> (k a c Nothing))
58-- XXX orphan instance, use a newtype instead?
59instance (Monad m, MonadRecorder m)
60 => MonadRecorder (StateT Context m) where
61 getJournal = lift getJournal
62 putJournal = lift . putJournal
65deriving instance (Monad m, MonadRecorder m)
66 => MonadRecorder (AsynclyT m)
72------------------------------------------------------------------------------
74------------------------------------------------------------------------------
76-- | Run an 'AsynclyT m' computation, wait for it to finish and discard the
78{-# INLINABLE runAsynclyLogged #-}
79runAsynclyLogged :: MonadAsync m
80 => Maybe (IORef [Recording]) -> AsynclyT m a -> m ()
81runAsynclyLogged lref m = run Nothing m
87 yield _ c r = maybe stop (\x -> (run c x)) r
88 run ct mx = (runAsyncT mx) ct stop yield
90runAsyncly :: MonadAsync m => AsynclyT m a -> m ()
91runAsyncly m = runAsynclyLogged Nothing m
93-- | Run an 'AsynclyT m' computation and collect the results generated by each
94-- thread of the computation in a list.
95{-# INLINABLE toList #-}
96toList :: MonadAsync m => AsynclyT m a -> m [a]
97toList m = run Nothing m
104 yield a c r = maybe (done a) (\x -> liftM (a :) (run c x)) r
107 run ctx mx = (runAsyncT mx) ctx stop yield
109-- | Run a given function concurrently on the list and collect the results.
111for :: MonadAsync m => [a] -> (a -> AsyncT m b) -> AsynclyT m b
112for xs f = foldr (<|>) empty $ map f xs
114-- XXX rename to fromList?
115{-# INLINABLE each #-}
116each :: MonadAsync m => [a] -> AsynclyT m a
117each xs = foldr (<>) empty $ map return xs
119------------------------------------------------------------------------------
121------------------------------------------------------------------------------
123-- | Compose a computation using previously captured logs
124playRecording :: (MonadAsync m, MonadRecorder m)
125 => AsynclyT m a -> Recording -> AsynclyT m a
126playRecording m recording = play recording >> m
128-- | Resume an 'AsyncT' computation using previously recorded logs. The
129-- recording consists of a list of journals one for each thread in the
131playRecordings :: (MonadAsync m, MonadRecorder m)
132 => AsynclyT m a -> [Recording] -> AsynclyT m a
133playRecordings m logs = each logs >>= playRecording m
136-- | Run an 'AsyncT' computation with recording enabled, wait for it to finish
137-- returning results for completed threads and recordings for paused threads.
138toListRecorded :: (MonadAsync m, MonadCatch m)
139 => AsynclyT m a -> m ([a], [Recording])
141 resultsRef <- liftIO $ newIORef []
142 lref <- liftIO $ newIORef []
143 waitAsync (gatherResult resultsRef) (Just lref) m
144 res <- liftIO $ readIORef resultsRef
145 logs <- liftIO $ readIORef lref
149-- | Run an 'AsyncT' computation with recording enabled, wait for it to finish
150-- and discard the results and return the recordings for paused threads, if
152runAsynclyRecorded :: MonadAsync m => AsynclyT (RecorderT m) a -> m [Recording]
153runAsynclyRecorded m = do
154 lref <- liftIO $ newIORef []
155 runRecorderT blank (runAsynclyLogged (Just lref) m)
156 logs <- liftIO $ readIORef lref
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list