@@ -14,10 +14,6 @@ module Asyncly.AsyncT
1414 ( AsyncT (..)
1515 , MonadAsync
1616 , ChildEvent(..)
17− , Loggable
18− , Log (..)
19− , CtxLog (..)
20− , LogEntry (..)
2117 , Context(..)
2218 , initContext
2319 , Location(..)
@@ -25,6 +21,7 @@ module Asyncly.AsyncT
2521 , setLocation
2622 , threads
2723 , waitForChildren
24+ , handleResult
2825
2926 , async
3027 , makeAsync
@@ -48,7 +45,8 @@ import Control.Concurrent (ThreadId, forkIO, killThread,
4845import Control.Concurrent.STM (TChan, atomically, newTChan,
4946 readTChan, tryReadTChan,
5047 writeTChan)
51−import Control.Exception (SomeException (..))
48+import Control.Exception (fromException,
49+ SomeException (..))
5250import qualified Control.Exception.Lifted as EL
5351import Control.Monad (ap, mzero, when)
5452import Control.Monad.Base (MonadBase (..), liftBaseDefault)
@@ -69,36 +67,12 @@ import Data.Maybe (isJust)
6967import GHC.Prim (Any)
7068import Unsafe.Coerce (unsafeCoerce)
7169
70+import Control.Monad.Trans.Recorder (MonadRecorder(..), Paused(..),
71+ Recording)
7272--import Debug.Trace (traceM)
7373
7474newtype AsyncT m a = AsyncT { runAsyncT :: StateT Context m (Maybe a) }
7575
76−------------------------------------------------------------------------------
77−-- Log types
78−------------------------------------------------------------------------------
79−
80−-- Logging is done using the 'logged' combinator. It remembers the last value
81−-- returned by a given computation and replays it the next time the computation
82−-- is resumed. However this could be problematic if we do not annotate all
83−-- impure computations. The program can take a different path due to a
84−-- non-logged computation returning a different value. In that case we may
85−-- replay a wrong value. To detect this we can use a unique id for each logging
86−-- site and abort if the id does not match on replay.
87−
88−-- | Constraint type synonym for a value that can be logged.
89−type Loggable a = (Show a, Read a)
90−
91−-- XXX remove the Maybe as we never log Nothing
92−data LogEntry =
93− Executing -- we are inside this computation, not yet done
94− | Result (Maybe String) -- computation done, we have the result to replay
95− deriving (Read, Show)
96−
97−data Log = Log [LogEntry] deriving Show
98−
99−-- log entries and replay entries
100−data CtxLog = CtxLog [LogEntry] [LogEntry] deriving (Read, Show)
101−
10276------------------------------------------------------------------------------
10377-- Parent child thread communication types
10478------------------------------------------------------------------------------
@@ -117,12 +91,10 @@ data Context = Context
11791
11892 -- a -> AsyncT m b
11993 continuation :: Any
120− -- Logging is done by the 'logged' primitive in this journal only if logsRef
121− -- exists.
122− , journal :: CtxLog
12394
12495 -- When we suspend we save the logs in this IORef and exit.
125− , logsRef :: Maybe (IORef [Log])
96+ , logsRef :: Maybe (IORef [Recording])
97+ -- XXX this functionality can be in a separate layer?
12698 , location :: Location
12799
128100 ---------------------------------------------------------------------------
@@ -175,11 +147,10 @@ initContext
175147 -> IORef [ThreadId]
176148 -> IORef Int
177149 -> (a -> AsyncT m a)
178− -> Maybe (IORef [Log])
150+ -> Maybe (IORef [Recording])
179151 -> Context
180152initContext childChan pending credit finalizer lref =
181153 Context { continuation = unsafeCoerce finalizer
182− , journal = CtxLog [] []
183154 , logsRef = lref
184155 , location = Worker
185156 , childChannel = unsafeCoerce childChan
@@ -318,6 +289,28 @@ signalQSemB sem = atomicModifyIORef sem $ \n -> (n + 1, ())
318289-- high.
319290--
320291
292+handleResult :: MonadIO m
293+ => Context -> Either SomeException a -> m (Maybe SomeException)
294+handleResult ctx res =
295+ case res of
296+ Left e -> do
297+ case fromException e of
298+ Just (Paused recording) ->
299+ maybe (passException e)
300+ (handleRecording recording) (logsRef ctx)
301+ Nothing -> passException e
302+ Right _ -> waitForChildren ctx
303+
304+ where
305+
306+ handleRecording recording ref = do
307+ liftIO $ atomicModifyIORef ref $ \logs -> (recording : logs, ())
308+ waitForChildren ctx
309+
310+ passException e = do
311+ liftIO $ readIORef (pendingThreads ctx) >>= mapM_ killThread
312+ return (Just e)
313+
321314forkFinally1 :: MonadAsync m
322315 => Context
323316 -> AsyncT m a
@@ -362,15 +355,9 @@ forkContext action = do
362355 }
363356
364357 beforeExit ctx pchan res = do
365− tid <- myThreadId
366− r <- case res of
367− Left e -> do
368− dbg $ "beforeExit: " ++ show tid ++ " caught exception"
369− liftIO $ readIORef (pendingThreads ctx) >>= mapM_ killThread
370− return (Just e)
371− Right _ -> waitForChildren ctx
372−
358+ r <- handleResult ctx res
373359 signalQSemB (threadCredit ctx)
360+ tid <- myThreadId
374361 liftIO $ atomically $ writeTChan pchan (ChildDone tid r)
375362
376363-- | Decide whether to resume the context in the same thread or a new thread
@@ -615,13 +602,6 @@ thenDiscard ma mb = AsyncT $ do
615602(>>*) :: MonadAsync m => AsyncT m a -> AsyncT m b -> AsyncT m a
616603(>>*) = thenDiscard
617604
618−------------------------------------------------------------------------------
619−-- MonadIO
620−------------------------------------------------------------------------------
621−
622−instance MonadAsync m => MonadIO (AsyncT m) where
623− liftIO mx = AsyncT $ liftIO mx >>= return . Just
624−
625605-------------------------------------------------------------------------------
626606-- AsyncT transformer
627607-------------------------------------------------------------------------------
@@ -629,6 +609,10 @@ instance MonadAsync m => MonadIO (AsyncT m) where
629609instance MonadTrans AsyncT where
630610 lift mx = AsyncT $ lift mx >>= return . Just
631611
612+-------------------------------------------------------------------------------
613+-- monad-control
614+-------------------------------------------------------------------------------
615+
632616instance MonadTransControl AsyncT where
633617 type StT AsyncT a = (Maybe a, Context)
634618 liftWith f = AsyncT $ StateT $ \s ->
@@ -648,5 +632,21 @@ instance (MonadBaseControl b m, MonadAsync m) => MonadBaseControl b (AsyncT m) w
648632 {-# INLINABLE liftBaseWith #-}
649633 {-# INLINABLE restoreM #-}
650634
635+------------------------------------------------------------------------------
636+-- Standard transformer instances
637+------------------------------------------------------------------------------
638+
639+instance MonadAsync m => MonadIO (AsyncT m) where
640+ liftIO = lift . liftIO
641+
651642instance MonadAsync m => MonadThrow (AsyncT m) where
652− throwM e = lift $ throwM e
643+ throwM = lift . throwM
644+
645+------------------------------------------------------------------------------
646+-- MonadRecorder
647+------------------------------------------------------------------------------
648+
649+instance (MonadAsync m, MonadRecorder m) => MonadRecorder (AsyncT m) where
650+ getJournal = lift getJournal
651+ putJournal = lift . putJournal
652+ play = lift . play