@@ -14,10 +14,6 @@ module Asyncly.AsyncT
14 ( AsyncT (..)
14 ( AsyncT (..)
15 , MonadAsync
15 , MonadAsync
16 , ChildEvent(..)
16 , ChildEvent(..)
21 , Context(..)
17 , Context(..)
22 , initContext
18 , initContext
23 , Location(..)
19 , Location(..)
@@ -25,6 +21,7 @@ module Asyncly.AsyncT
25 , setLocation
21 , setLocation
27 , waitForChildren
23 , waitForChildren
30 , makeAsync
27 , makeAsync
@@ -48,7 +45,8 @@ import Control.Concurrent (ThreadId, forkIO, killThread,
48import Control.Concurrent.STM (TChan, atomically, newTChan,
45import Control.Concurrent.STM (TChan, atomically, newTChan,
49 readTChan, tryReadTChan,
46 readTChan, tryReadTChan,
50 writeTChan)
47 writeTChan)
51−import Control.Exception (SomeException (..))
48+import Control.Exception (fromException,
52import qualified Control.Exception.Lifted as EL
50import qualified Control.Exception.Lifted as EL
53import Control.Monad (ap, mzero, when)
51import Control.Monad (ap, mzero, when)
54import Control.Monad.Base (MonadBase (..), liftBaseDefault)
52import Control.Monad.Base (MonadBase (..), liftBaseDefault)
@@ -69,36 +67,12 @@ import Data.Maybe (isJust)
69import GHC.Prim (Any)
67import GHC.Prim (Any)
70import Unsafe.Coerce (unsafeCoerce)
68import Unsafe.Coerce (unsafeCoerce)
70+import Control.Monad.Trans.Recorder (MonadRecorder(..), Paused(..),
72--import Debug.Trace (traceM)
72--import Debug.Trace (traceM)
74newtype AsyncT m a = AsyncT { runAsyncT :: StateT Context m (Maybe a) }
74newtype AsyncT m a = AsyncT { runAsyncT :: StateT Context m (Maybe a) }
76−------------------------------------------------------------------------------
78−------------------------------------------------------------------------------
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.
88−-- | Constraint type synonym for a value that can be logged.
89−type Loggable a = (Show a, Read a)
91−-- XXX remove the Maybe as we never log Nothing
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)
97−data Log = Log [LogEntry] deriving Show
99−-- log entries and replay entries
100−data CtxLog = CtxLog [LogEntry] [LogEntry] deriving (Read, Show)
102------------------------------------------------------------------------------
76------------------------------------------------------------------------------
103-- Parent child thread communication types
77-- Parent child thread communication types
104------------------------------------------------------------------------------
78------------------------------------------------------------------------------
@@ -117,12 +91,10 @@ data Context = Context
118 -- a -> AsyncT m b
92 -- a -> AsyncT m b
119 continuation :: Any
93 continuation :: Any
120− -- Logging is done by the 'logged' primitive in this journal only if logsRef
124 -- When we suspend we save the logs in this IORef and exit.
95 -- 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?
126 , location :: Location
98 , location :: Location
128 ---------------------------------------------------------------------------
100 ---------------------------------------------------------------------------
@@ -175,11 +147,10 @@ initContext
175 -> IORef [ThreadId]
147 -> IORef [ThreadId]
176 -> IORef Int
148 -> IORef Int
177 -> (a -> AsyncT m a)
149 -> (a -> AsyncT m a)
178− -> Maybe (IORef [Log])
150+ -> Maybe (IORef [Recording])
179 -> Context
151 -> Context
180initContext childChan pending credit finalizer lref =
152initContext childChan pending credit finalizer lref =
181 Context { continuation = unsafeCoerce finalizer
153 Context { continuation = unsafeCoerce finalizer
182− , journal = CtxLog [] []
183 , logsRef = lref
154 , logsRef = lref
184 , location = Worker
155 , location = Worker
185 , childChannel = unsafeCoerce childChan
156 , childChannel = unsafeCoerce childChan
@@ -318,6 +289,28 @@ signalQSemB sem = atomicModifyIORef sem $ \n -> (n + 1, ())
292+handleResult :: MonadIO m
293+ => Context -> Either SomeException a -> m (Maybe SomeException)
294+handleResult ctx res =
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
306+ handleRecording recording ref = do
307+ liftIO $ atomicModifyIORef ref $ \logs -> (recording : logs, ())
310+ passException e = do
311+ liftIO $ readIORef (pendingThreads ctx) >>= mapM_ killThread
321forkFinally1 :: MonadAsync m
314forkFinally1 :: MonadAsync m
322 => Context
315 => Context
323 -> AsyncT m a
316 -> AsyncT m a
@@ -362,15 +355,9 @@ forkContext action = do
364 beforeExit ctx pchan res = do
357 beforeExit ctx pchan res = do
365− tid <- myThreadId
358+ r <- handleResult ctx res
368− dbg $ "beforeExit: " ++ show tid ++ " caught exception"
369− liftIO $ readIORef (pendingThreads ctx) >>= mapM_ killThread
371− Right _ -> waitForChildren ctx
373 signalQSemB (threadCredit ctx)
359 signalQSemB (threadCredit ctx)
374 liftIO $ atomically $ writeTChan pchan (ChildDone tid r)
361 liftIO $ atomically $ writeTChan pchan (ChildDone tid r)
376-- | Decide whether to resume the context in the same thread or a new thread
363-- | Decide whether to resume the context in the same thread or a new thread
@@ -615,13 +602,6 @@ thenDiscard ma mb = AsyncT $ do
615(>>*) :: MonadAsync m => AsyncT m a -> AsyncT m b -> AsyncT m a
602(>>*) :: MonadAsync m => AsyncT m a -> AsyncT m b -> AsyncT m a
616(>>*) = thenDiscard
603(>>*) = thenDiscard
618−------------------------------------------------------------------------------
620−------------------------------------------------------------------------------
622−instance MonadAsync m => MonadIO (AsyncT m) where
623− liftIO mx = AsyncT $ liftIO mx >>= return . Just
625-------------------------------------------------------------------------------
605-------------------------------------------------------------------------------
626-- AsyncT transformer
606-- AsyncT transformer
627-------------------------------------------------------------------------------
607-------------------------------------------------------------------------------
@@ -629,6 +609,10 @@ instance MonadAsync m => MonadIO (AsyncT m) where
629instance MonadTrans AsyncT where
609instance MonadTrans AsyncT where
630 lift mx = AsyncT $ lift mx >>= return . Just
610 lift mx = AsyncT $ lift mx >>= return . Just
612+-------------------------------------------------------------------------------
614+-------------------------------------------------------------------------------
632instance MonadTransControl AsyncT where
616instance MonadTransControl AsyncT where
633 type StT AsyncT a = (Maybe a, Context)
617 type StT AsyncT a = (Maybe a, Context)
634 liftWith f = AsyncT $ StateT $ \s ->
618 liftWith f = AsyncT $ StateT $ \s ->
@@ -648,5 +632,21 @@ instance (MonadBaseControl b m, MonadAsync m) => MonadBaseControl b (AsyncT m) w
648 {-# INLINABLE liftBaseWith #-}
632 {-# INLINABLE liftBaseWith #-}
649 {-# INLINABLE restoreM #-}
633 {-# INLINABLE restoreM #-}
635+------------------------------------------------------------------------------
636+-- Standard transformer instances
637+------------------------------------------------------------------------------
639+instance MonadAsync m => MonadIO (AsyncT m) where
640+ liftIO = lift . liftIO
651instance MonadAsync m => MonadThrow (AsyncT m) where
642instance MonadAsync m => MonadThrow (AsyncT m) where
652− throwM e = lift $ throwM e
643+ throwM = lift . throwM
645+------------------------------------------------------------------------------
647+------------------------------------------------------------------------------
649+instance (MonadAsync m, MonadRecorder m) => MonadRecorder (AsyncT m) where
650+ getJournal = lift getJournal
651+ putJournal = lift . putJournal