module Simulation.Aivika.Trans.Internal.Cont
(ContCancellationSource,
ContParams,
ContCancellation(..),
Cont(..),
newContCancellationSource,
contCancellationInitiated,
contCancellationInitiate,
contCancellationInitiating,
contCancellationBind,
contCancellationConnect,
invokeCont,
runCont,
rerunCont,
spawnCont,
contParallel,
contParallel_,
catchCont,
finallyCont,
throwCont,
resumeCont,
resumeECont,
contCanceled,
contFreeze,
contAwait) where
import Data.Array
import Data.Monoid
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Applicative
import Simulation.Aivika.Trans.Session
import Simulation.Aivika.Trans.ProtoRef
import Simulation.Aivika.Trans.ProtoArray
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Trans.Comp
import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Internal.Parameter
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Dynamics
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Internal.Signal
data ContCancellation = CancelTogether
| CancelChildAfterParent
| CancelParentAfterChild
| CancelInIsolation
data ContCancellationSource m =
ContCancellationSource { contCancellationInitiatedRef :: ProtoRef m Bool,
contCancellationActivatedRef :: ProtoRef m Bool,
contCancellationInitiatingSource :: SignalSource m ()
}
newContCancellationSource :: MonadComp m => Simulation m (ContCancellationSource m)
newContCancellationSource =
Simulation $ \r ->
do let sn = runSession r
r1 <- newProtoRef sn False
r2 <- newProtoRef sn False
s <- invokeSimulation r newSignalSource
return ContCancellationSource { contCancellationInitiatedRef = r1,
contCancellationActivatedRef = r2,
contCancellationInitiatingSource = s
}
contCancellationInitiating :: ContCancellationSource m -> Signal m ()
contCancellationInitiating =
publishSignal . contCancellationInitiatingSource
contCancellationInitiated :: MonadComp m => ContCancellationSource m -> (Event m Bool)
contCancellationInitiated x =
Event $ \p -> readProtoRef (contCancellationInitiatedRef x)
contCancellationActivated :: MonadComp m => ContCancellationSource m -> m Bool
contCancellationActivated =
readProtoRef . contCancellationActivatedRef
contCancellationDeactivate :: MonadComp m => ContCancellationSource m -> m ()
contCancellationDeactivate x =
writeProtoRef (contCancellationActivatedRef x) False
contCancellationBind :: MonadComp m => ContCancellationSource m -> [ContCancellationSource m] -> Event m (DisposableEvent m)
contCancellationBind x ys =
Event $ \p ->
do hs1 <- forM ys $ \y ->
invokeEvent p $
handleSignal (contCancellationInitiating x) $ \_ ->
contCancellationInitiate y
hs2 <- forM ys $ \y ->
invokeEvent p $
handleSignal (contCancellationInitiating y) $ \_ ->
contCancellationInitiate x
return $ mconcat hs1 <> mconcat hs2
contCancellationConnect :: MonadComp m
=> ContCancellationSource m
-> ContCancellation
-> ContCancellationSource m
-> Event m (DisposableEvent m)
contCancellationConnect parent cancellation child =
Event $ \p ->
do let m1 =
handleSignal (contCancellationInitiating parent) $ \_ ->
contCancellationInitiate child
m2 =
handleSignal (contCancellationInitiating child) $ \_ ->
contCancellationInitiate parent
h1 <-
case cancellation of
CancelTogether -> invokeEvent p m1
CancelChildAfterParent -> invokeEvent p m1
CancelParentAfterChild -> return mempty
CancelInIsolation -> return mempty
h2 <-
case cancellation of
CancelTogether -> invokeEvent p m2
CancelChildAfterParent -> return mempty
CancelParentAfterChild -> invokeEvent p m2
CancelInIsolation -> return mempty
return $ h1 <> h2
contCancellationInitiate :: MonadComp m => ContCancellationSource m -> Event m ()
contCancellationInitiate x =
Event $ \p ->
do f <- readProtoRef (contCancellationInitiatedRef x)
unless f $
do writeProtoRef (contCancellationInitiatedRef x) True
writeProtoRef (contCancellationActivatedRef x) True
invokeEvent p $ triggerSignal (contCancellationInitiatingSource x) ()
newtype Cont m a = Cont (ContParams m a -> Event m ())
data ContParams m a =
ContParams { contCont :: a -> Event m (),
contAux :: ContParamsAux m }
data ContParamsAux m =
ContParamsAux { contECont :: SomeException -> Event m (),
contCCont :: () -> Event m (),
contCancelSource :: ContCancellationSource m,
contCancelFlag :: m Bool,
contCatchFlag :: Bool }
instance MonadComp m => Monad (Cont m) where
return a =
Cont $ \c ->
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ contCont c a
(Cont m) >>= k =
Cont $ \c ->
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ m $
let cont a = invokeCont c (k a)
in c { contCont = cont }
instance MonadCompTrans Cont where
liftComp m =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching m p c
else liftWithoutCatching m p c
instance ParameterLift Cont where
liftParameter (Parameter m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching (m $ pointRun p) p c
else liftWithoutCatching (m $ pointRun p) p c
instance SimulationLift Cont where
liftSimulation (Simulation m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching (m $ pointRun p) p c
else liftWithoutCatching (m $ pointRun p) p c
instance DynamicsLift Cont where
liftDynamics (Dynamics m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching (m p) p c
else liftWithoutCatching (m p) p c
instance EventLift Cont where
liftEvent (Event m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching (m p) p c
else liftWithoutCatching (m p) p c
instance (MonadComp m, MonadIO m) => MonadIO (Cont m) where
liftIO m =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching (liftIO m) p c
else liftWithoutCatching (liftIO m) p c
instance MonadComp m => Functor (Cont m) where
fmap = liftM
instance MonadComp m => Applicative (Cont m) where
pure = return
(<*>) = ap
invokeCont :: ContParams m a -> Cont m a -> Event m ()
invokeCont p (Cont m) = m p
cancelCont :: MonadComp m => Point m -> ContParams m a -> m ()
cancelCont p c =
do contCancellationDeactivate (contCancelSource $ contAux c)
invokeEvent p $ (contCCont $ contAux c) ()
callCont :: MonadComp m => (a -> Cont m b) -> a -> ContParams m b -> Event m ()
callCont k a c =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ invokeCont c (k a)
catchCont :: (MonadComp m, Exception e) => Cont m a -> (e -> Cont m a) -> Cont m a
catchCont (Cont m) h =
Cont $ \c0 ->
Event $ \p ->
do let c = c0 { contAux = (contAux c0) { contCatchFlag = True } }
z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ m $
let econt e0 =
case fromException e0 of
Just e -> callCont h e c
Nothing -> (contECont . contAux $ c) e0
in c { contAux = (contAux c) { contECont = econt } }
finallyCont :: MonadComp m => Cont m a -> Cont m b -> Cont m a
finallyCont (Cont m) (Cont m') =
Cont $ \c0 ->
Event $ \p ->
do let c = c0 { contAux = (contAux c0) { contCatchFlag = True } }
z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ m $
let cont a =
Event $ \p ->
invokeEvent p $ m' $
let cont b = contCont c a
in c { contCont = cont }
econt e =
Event $ \p ->
invokeEvent p $ m' $
let cont b = (contECont . contAux $ c) e
in c { contCont = cont }
ccont () =
Event $ \p ->
invokeEvent p $ m' $
let cont b = (contCCont . contAux $ c) ()
econt e = (contCCont . contAux $ c) ()
in c { contCont = cont,
contAux = (contAux c) { contECont = econt } }
in c { contCont = cont,
contAux = (contAux c) { contECont = econt,
contCCont = ccont } }
throwCont :: (MonadComp m, Exception e) => e -> Cont m a
throwCont = liftEvent . throwEvent
runCont :: MonadComp m
=> Cont m a
-> (a -> Event m ())
-> (SomeException -> Event m ())
-> (() -> Event m ())
-> ContCancellationSource m
-> Bool
-> Event m ()
runCont (Cont m) cont econt ccont cancelSource catchFlag =
m ContParams { contCont = cont,
contAux =
ContParamsAux { contECont = econt,
contCCont = ccont,
contCancelSource = cancelSource,
contCancelFlag = contCancellationActivated cancelSource,
contCatchFlag = catchFlag } }
liftWithoutCatching :: MonadComp m => m a -> Point m -> ContParams m a -> m ()
liftWithoutCatching m p c =
do z <- contCanceled c
if z
then cancelCont p c
else do a <- m
invokeEvent p $ contCont c a
liftWithCatching :: MonadComp m => m a -> Point m -> ContParams m a -> m ()
liftWithCatching m p c =
do z <- contCanceled c
if z
then cancelCont p c
else do let s = runSession $ pointRun p
aref <- newProtoRef s undefined
eref <- newProtoRef s Nothing
catchComp
(m >>= writeProtoRef aref)
(writeProtoRef eref . Just)
e <- readProtoRef eref
case e of
Nothing ->
do a <- readProtoRef aref
invokeEvent p $ contCont c a
Just e ->
invokeEvent p $ (contECont . contAux) c e
resumeCont :: MonadComp m => ContParams m a -> a -> Event m ()
resumeCont c a =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ contCont c a
resumeECont :: MonadComp m => ContParams m a -> SomeException -> Event m ()
resumeECont c e =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ (contECont $ contAux c) e
contCanceled :: ContParams m a -> m Bool
contCanceled c = contCancelFlag $ contAux c
contParallel :: MonadComp m
=> [(Cont m a, ContCancellationSource m)]
-> Cont m [a]
contParallel xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
s = runSession $ pointRun p
worker =
do results <- newProtoArray_ s n
counter <- newProtoRef s 0
catchRef <- newProtoRef s Nothing
hs <- invokeEvent p $
contCancellationBind (contCancelSource $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- readProtoRef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- contCanceled c
f2 <- readProtoRef catchRef
case (f1, f2) of
(False, Nothing) ->
do rs <- protoArrayToList results
invokeEvent p $ resumeCont c rs
(False, Just e) ->
invokeEvent p $ resumeECont c e
(True, _) ->
cancelCont p c
cont i a =
Event $ \p ->
do modifyProtoRef counter (+ 1)
writeProtoArray results i a
invokeEvent p propagate
econt e =
Event $ \p ->
do modifyProtoRef counter (+ 1)
r <- readProtoRef catchRef
case r of
Nothing -> writeProtoRef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do modifyProtoRef counter (+ 1)
invokeEvent p propagate
forM_ (zip [0..n1] xs) $ \(i, (x, cancelSource)) ->
invokeEvent p $
runCont x (cont i) econt ccont cancelSource (contCatchFlag $ contAux c)
z <- contCanceled c
if z
then cancelCont p c
else if n == 0
then invokeEvent p $ contCont c []
else worker
contParallel_ :: MonadComp m
=> [(Cont m a, ContCancellationSource m)]
-> Cont m ()
contParallel_ xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
s = runSession $ pointRun p
worker =
do counter <- newProtoRef s 0
catchRef <- newProtoRef s Nothing
hs <- invokeEvent p $
contCancellationBind (contCancelSource $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- readProtoRef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- contCanceled c
f2 <- readProtoRef catchRef
case (f1, f2) of
(False, Nothing) ->
invokeEvent p $ resumeCont c ()
(False, Just e) ->
invokeEvent p $ resumeECont c e
(True, _) ->
cancelCont p c
cont i a =
Event $ \p ->
do modifyProtoRef counter (+ 1)
invokeEvent p propagate
econt e =
Event $ \p ->
do modifyProtoRef counter (+ 1)
r <- readProtoRef catchRef
case r of
Nothing -> writeProtoRef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do modifyProtoRef counter (+ 1)
invokeEvent p propagate
forM_ (zip [0..n1] xs) $ \(i, (x, cancelSource)) ->
invokeEvent p $
runCont x (cont i) econt ccont cancelSource (contCatchFlag $ contAux c)
z <- contCanceled c
if z
then cancelCont p c
else if n == 0
then invokeEvent p $ contCont c ()
else worker
rerunCont :: MonadComp m => Cont m a -> ContCancellationSource m -> Cont m a
rerunCont x cancelSource =
Cont $ \c ->
Event $ \p ->
do let worker =
do hs <- invokeEvent p $
contCancellationBind (contCancelSource $ contAux c) [cancelSource]
let cont a =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
invokeEvent p $ resumeCont c a
econt e =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
invokeEvent p $ resumeECont c e
ccont e =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
cancelCont p c
invokeEvent p $
runCont x cont econt ccont cancelSource (contCatchFlag $ contAux c)
z <- contCanceled c
if z
then cancelCont p c
else worker
spawnCont :: MonadComp m => ContCancellation -> Cont m () -> ContCancellationSource m -> Cont m ()
spawnCont cancellation x cancelSource =
Cont $ \c ->
Event $ \p ->
do let worker =
do hs <- invokeEvent p $
contCancellationConnect
(contCancelSource $ contAux c) cancellation cancelSource
let cont a =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
econt e =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
invokeEvent p $ throwEvent e
ccont e =
Event $ \p ->
do invokeEvent p $ disposeEvent hs
invokeEvent p $
enqueueEvent (pointTime p) $
runCont x cont econt ccont cancelSource False
invokeEvent p $
resumeCont c ()
z <- contCanceled c
if z
then cancelCont p c
else worker
contFreeze :: MonadComp m => ContParams m a -> Event m (Event m (Maybe (ContParams m a)))
contFreeze c =
Event $ \p ->
do let s = runSession $ pointRun p
rh <- newProtoRef s Nothing
rc <- newProtoRef s $ Just c
h <- invokeEvent p $
handleSignal (contCancellationInitiating $
contCancelSource $
contAux c) $ \a ->
Event $ \p ->
do h <- readProtoRef rh
case h of
Nothing ->
error "The handler was lost: contFreeze."
Just h ->
do invokeEvent p $ disposeEvent h
c <- readProtoRef rc
case c of
Nothing -> return ()
Just c ->
do writeProtoRef rc Nothing
invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do z <- contCanceled c
when z $ cancelCont p c
writeProtoRef rh (Just h)
return $
Event $ \p ->
do invokeEvent p $ disposeEvent h
c <- readProtoRef rc
writeProtoRef rc Nothing
return c
contAwait :: MonadComp m => Signal m a -> Cont m a
contAwait signal =
Cont $ \c ->
Event $ \p ->
do let s = runSession $ pointRun p
c <- invokeEvent p $ contFreeze c
r <- newProtoRef s Nothing
h <- invokeEvent p $
handleSignal signal $
\a -> Event $
\p -> do x <- readProtoRef r
case x of
Nothing ->
error "The signal was lost: contAwait."
Just x ->
do invokeEvent p $ disposeEvent x
c <- invokeEvent p c
case c of
Nothing -> return ()
Just c ->
invokeEvent p $ resumeCont c a
writeProtoRef r $ Just h