module Simulation.Aivika.Trans.Internal.Cont
(ContParams,
ContCancellation(..),
Cont(..),
ContId,
ContEvent(..),
FrozenCont,
newContId,
contSignal,
contCancellationInitiated,
contCancellationInitiate,
contCancellationInitiating,
contCancellationActivated,
contCancellationBind,
contCancellationConnect,
contPreemptionBegun,
contPreemptionBegin,
contPreemptionBeginning,
contPreemptionEnd,
contPreemptionEnding,
invokeCont,
runCont,
rerunCont,
spawnCont,
contParallel,
contParallel_,
catchCont,
finallyCont,
throwCont,
resumeCont,
resumeECont,
reenterCont,
freezeCont,
freezeContReentering,
unfreezeCont,
substituteCont,
contCanceled,
contAwait,
transferCont,
traceCont) where
import Data.Array
import Data.Monoid
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Applicative
import Debug.Trace (trace)
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Trans.Comp
import Simulation.Aivika.Trans.DES
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.Signal
data ContCancellation = CancelTogether
| CancelChildAfterParent
| CancelParentAfterChild
| CancelInIsolation
data ContId m =
ContId { contCancellationInitiatedRef :: Ref m Bool,
contCancellationActivatedRef :: Ref m Bool,
contPreemptionCountRef :: Ref m Int,
contSignalSource :: SignalSource m ContEvent
}
instance MonadDES m => Eq (ContId m) where
x == y = contCancellationInitiatedRef x == contCancellationInitiatedRef y
data ContEvent = ContCancellationInitiating
| ContPreemptionBeginning
| ContPreemptionEnding
deriving (Eq, Ord, Show)
newContId :: MonadDES m => Simulation m (ContId m)
newContId =
Simulation $ \r ->
do r1 <- invokeSimulation r $ newRef False
r2 <- invokeSimulation r $ newRef False
r3 <- invokeSimulation r $ newRef 0
s <- invokeSimulation r newSignalSource
return ContId { contCancellationInitiatedRef = r1,
contCancellationActivatedRef = r2,
contPreemptionCountRef = r3,
contSignalSource = s
}
contSignal :: ContId m -> Signal m ContEvent
contSignal = publishSignal . contSignalSource
contCancellationInitiating :: MonadDES m => ContId m -> Signal m ()
contCancellationInitiating =
filterSignal_ (ContCancellationInitiating ==) . contSignal
contCancellationInitiated :: MonadDES m => ContId m -> Event m Bool
contCancellationInitiated =
readRef . contCancellationInitiatedRef
contCancellationActivated :: MonadDES m => ContId m -> Event m Bool
contCancellationActivated =
readRef . contCancellationActivatedRef
contCancellationDeactivate :: MonadDES m => ContId m -> Event m ()
contCancellationDeactivate x =
writeRef (contCancellationActivatedRef x) False
contCancellationBind :: MonadDES m => ContId m -> [ContId 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 :: MonadDES m
=> ContId m
-> ContCancellation
-> ContId 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 :: MonadDES m => ContId m -> Event m ()
contCancellationInitiate x =
Event $ \p ->
do f <- invokeEvent p $ readRef (contCancellationInitiatedRef x)
unless f $
do invokeEvent p $ writeRef (contCancellationInitiatedRef x) True
invokeEvent p $ writeRef (contCancellationActivatedRef x) True
invokeEvent p $ triggerSignal (contSignalSource x) ContCancellationInitiating
contPreemptionBegin :: MonadDES m => ContId m -> Event m ()
contPreemptionBegin x =
Event $ \p ->
do f <- invokeEvent p $ readRef (contCancellationInitiatedRef x)
unless f $
do n <- invokeEvent p $ readRef (contPreemptionCountRef x)
let n' = n + 1
n' `seq` invokeEvent p $ writeRef (contPreemptionCountRef x) n'
when (n == 0) $
invokeEvent p $
triggerSignal (contSignalSource x) ContPreemptionBeginning
contPreemptionEnd :: MonadDES m => ContId m -> Event m ()
contPreemptionEnd x =
Event $ \p ->
do f <- invokeEvent p $ readRef (contCancellationInitiatedRef x)
unless f $
do n <- invokeEvent p $ readRef (contPreemptionCountRef x)
let n' = n 1
n' `seq` invokeEvent p $ writeRef (contPreemptionCountRef x) n'
when (n' == 0) $
invokeEvent p $
triggerSignal (contSignalSource x) ContPreemptionEnding
contPreemptionBeginning :: MonadDES m => ContId m -> Signal m ()
contPreemptionBeginning =
filterSignal_ (ContPreemptionBeginning ==) . contSignal
contPreemptionEnding :: MonadDES m => ContId m -> Signal m ()
contPreemptionEnding =
filterSignal_ (ContPreemptionEnding ==) . contSignal
contPreemptionBegun :: MonadDES m => ContId m -> Event m Bool
contPreemptionBegun x =
Event $ \p ->
do n <- invokeEvent p $ readRef (contPreemptionCountRef x)
return (n > 0)
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 (),
contId :: ContId m,
contCancelFlag :: Event m Bool,
contCatchFlag :: Bool }
instance MonadDES m => Monad (Cont m) where
return a =
Cont $ \c ->
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else invokeEvent p $ contCont c a
(Cont m) >>= k =
Cont $ \c ->
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else invokeEvent p $ m $
let cont a = invokeCont c (k a)
in c { contCont = cont }
instance MonadDES m => MonadCompTrans Cont m where
liftComp m =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftWithCatching m p c
else liftWithoutCatching m p c
instance MonadDES m => ParameterLift Cont m 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 MonadDES m => SimulationLift Cont m 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 MonadDES m => DynamicsLift Cont m 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 MonadDES m => EventLift Cont m 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 (MonadDES 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 MonadDES m => Functor (Cont m) where
fmap = liftM
instance MonadDES m => Applicative (Cont m) where
pure = return
(<*>) = ap
invokeCont :: ContParams m a -> Cont m a -> Event m ()
invokeCont p (Cont m) = m p
cancelCont :: MonadDES m => ContParams m a -> Event m ()
cancelCont c =
Event $ \p ->
do invokeEvent p $ contCancellationDeactivate (contId $ contAux c)
invokeEvent p $ (contCCont $ contAux c) ()
callCont :: MonadDES m => (a -> Cont m b) -> a -> ContParams m b -> Event m ()
callCont k a c =
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else invokeEvent p $ invokeCont c (k a)
catchCont :: (MonadDES 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 <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont 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 :: MonadDES 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 <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont 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 :: (MonadDES m, Exception e) => e -> Cont m a
throwCont = liftEvent . throwEvent
runCont :: MonadDES m
=> Cont m a
-> (a -> Event m ())
-> (SomeException -> Event m ())
-> (() -> Event m ())
-> ContId m
-> Bool
-> Event m ()
runCont (Cont m) cont econt ccont cid catchFlag =
m ContParams { contCont = cont,
contAux =
ContParamsAux { contECont = econt,
contCCont = ccont,
contId = cid,
contCancelFlag = contCancellationActivated cid,
contCatchFlag = catchFlag } }
liftWithoutCatching :: MonadDES m => m a -> Point m -> ContParams m a -> m ()
liftWithoutCatching m p c =
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else do a <- m
invokeEvent p $ contCont c a
liftWithCatching :: MonadDES m => m a -> Point m -> ContParams m a -> m ()
liftWithCatching m p c =
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else do let r = pointRun p
aref <- invokeSimulation r $ newRef undefined
eref <- invokeSimulation r $ newRef Nothing
catchComp
(m >>= invokeEvent p . writeRef aref)
(invokeEvent p . writeRef eref . Just)
e <- invokeEvent p $ readRef eref
case e of
Nothing ->
do a <- invokeEvent p $ readRef aref
invokeEvent p $ contCont c a
Just e ->
invokeEvent p $ (contECont . contAux) c e
resumeCont :: MonadDES m => ContParams m a -> a -> Event m ()
resumeCont c a =
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else invokeEvent p $ contCont c a
resumeECont :: MonadDES m => ContParams m a -> SomeException -> Event m ()
resumeECont c e =
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else invokeEvent p $ (contECont $ contAux c) e
contCanceled :: ContParams m a -> Event m Bool
contCanceled c = contCancelFlag $ contAux c
contParallel :: MonadDES m
=> [(Cont m a, ContId m)]
-> Cont m [a]
contParallel xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
r = pointRun p
worker =
do results <- forM [1..n] $ \i -> invokeSimulation r $ newRef undefined
counter <- invokeSimulation r $ newRef 0
catchRef <- invokeSimulation r $ newRef Nothing
hs <- invokeEvent p $
contCancellationBind (contId $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- invokeEvent p $ readRef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- invokeEvent p $ contCanceled c
f2 <- invokeEvent p $ readRef catchRef
case (f1, f2) of
(False, Nothing) ->
do rs <- forM results $ invokeEvent p . readRef
invokeEvent p $ resumeCont c rs
(False, Just e) ->
invokeEvent p $ resumeECont c e
(True, _) ->
invokeEvent p $ cancelCont c
cont result a =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
invokeEvent p $ writeRef result a
invokeEvent p propagate
econt e =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
r <- invokeEvent p $ readRef catchRef
case r of
Nothing -> invokeEvent p $ writeRef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
invokeEvent p propagate
forM_ (zip results xs) $ \(result, (x, cid)) ->
invokeEvent p $
runCont x (cont result) econt ccont cid (contCatchFlag $ contAux c)
z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else if n == 0
then invokeEvent p $ contCont c []
else worker
contParallel_ :: MonadDES m
=> [(Cont m a, ContId m)]
-> Cont m ()
contParallel_ xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
r = pointRun p
worker =
do counter <- invokeSimulation r $ newRef 0
catchRef <- invokeSimulation r $ newRef Nothing
hs <- invokeEvent p $
contCancellationBind (contId $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- invokeEvent p $ readRef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- invokeEvent p $ contCanceled c
f2 <- invokeEvent p $ readRef catchRef
case (f1, f2) of
(False, Nothing) ->
invokeEvent p $ resumeCont c ()
(False, Just e) ->
invokeEvent p $ resumeECont c e
(True, _) ->
invokeEvent p $ cancelCont c
cont a =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
invokeEvent p propagate
econt e =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
r <- invokeEvent p $ readRef catchRef
case r of
Nothing -> invokeEvent p $ writeRef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do invokeEvent p $ modifyRef counter (+ 1)
invokeEvent p propagate
forM_ (zip [0..n1] xs) $ \(i, (x, cid)) ->
invokeEvent p $
runCont x cont econt ccont cid (contCatchFlag $ contAux c)
z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else if n == 0
then invokeEvent p $ contCont c ()
else worker
rerunCont :: MonadDES m => Cont m a -> ContId m -> Cont m a
rerunCont x cid =
Cont $ \c ->
Event $ \p ->
do let worker =
do hs <- invokeEvent p $
contCancellationBind (contId $ contAux c) [cid]
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
invokeEvent p $ cancelCont c
invokeEvent p $
runCont x cont econt ccont cid (contCatchFlag $ contAux c)
z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else worker
spawnCont :: MonadDES m => ContCancellation -> Cont m () -> ContId m -> Cont m ()
spawnCont cancellation x cid =
Cont $ \c ->
Event $ \p ->
do let worker =
do hs <- invokeEvent p $
contCancellationConnect
(contId $ contAux c) cancellation cid
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 cid False
invokeEvent p $
resumeCont c ()
z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else worker
newtype FrozenCont m a =
FrozenCont { unfreezeCont :: Event m (Maybe (ContParams m a))
}
freezeCont :: MonadDES m => ContParams m a -> Event m (FrozenCont m a)
freezeCont c =
Event $ \p ->
do let r = pointRun p
rh <- invokeSimulation r $ newRef Nothing
rc <- invokeSimulation r $ newRef $ Just c
h <- invokeEvent p $
handleSignal (contCancellationInitiating $
contId $
contAux c) $ \e ->
Event $ \p ->
do h <- invokeEvent p $ readRef rh
case h of
Nothing ->
error "The handler was lost: freezeCont."
Just h ->
do invokeEvent p $ disposeEvent h
c <- invokeEvent p $ readRef rc
case c of
Nothing -> return ()
Just c ->
do invokeEvent p $ writeRef rc Nothing
invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
when z $ invokeEvent p $ cancelCont c
invokeEvent p $ writeRef rh (Just h)
return $
FrozenCont $
Event $ \p ->
do invokeEvent p $ disposeEvent h
c <- invokeEvent p $ readRef rc
invokeEvent p $ writeRef rc Nothing
return c
freezeContReentering :: MonadDES m => ContParams m a -> a -> Event m () -> Event m (FrozenCont m a)
freezeContReentering c a m =
Event $ \p ->
do let r = pointRun p
rh <- invokeSimulation r $ newRef Nothing
rc <- invokeSimulation r $ newRef $ Just c
h <- invokeEvent p $
handleSignal (contCancellationInitiating $
contId $ contAux c) $ \e ->
Event $ \p ->
do h <- invokeEvent p $ readRef rh
case h of
Nothing ->
error "The handler was lost: freezeContReentering."
Just h ->
do invokeEvent p $ disposeEvent h
c <- invokeEvent p $ readRef rc
case c of
Nothing -> return ()
Just c ->
do invokeEvent p $ writeRef rc Nothing
invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
when z $ invokeEvent p $ cancelCont c
invokeEvent p $ writeRef rh (Just h)
return $
FrozenCont $
Event $ \p ->
do invokeEvent p $ disposeEvent h
c <- invokeEvent p $ readRef rc
invokeEvent p $ writeRef rc Nothing
case c of
Nothing -> return Nothing
z @ (Just c) ->
do f <- invokeEvent p $
contPreemptionBegun $
contId $ contAux c
if not f
then return z
else do let c = c { contCont = \a -> m }
invokeEvent p $ sleepCont c a
return Nothing
reenterCont :: MonadDES m => ContParams m a -> a -> Event m ()
reenterCont c a =
Event $ \p ->
do f <- invokeEvent p $
contPreemptionBegun $
contId $ contAux c
if not f
then invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do f <- invokeEvent p $
contPreemptionBegun $
contId $ contAux c
if not f
then invokeEvent p $
resumeCont c a
else invokeEvent p $
sleepCont c a
else invokeEvent p $
sleepCont c a
sleepCont :: MonadDES m => ContParams m a -> a -> Event m ()
sleepCont c a =
Event $ \p ->
do let r = pointRun p
rh <- invokeSimulation r $ newRef Nothing
h <- invokeEvent p $
handleSignal (contSignal $
contId $ contAux c) $ \e ->
Event $ \p ->
do h <- invokeEvent p $ readRef rh
case h of
Nothing ->
error "The handler was lost: sleepCont."
Just h ->
do invokeEvent p $ disposeEvent h
case e of
ContCancellationInitiating ->
invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
when z $ invokeEvent p $ cancelCont c
ContPreemptionEnding ->
invokeEvent p $
enqueueEvent (pointTime p) $
reenterCont c a
ContPreemptionBeginning ->
error "The computation was already preempted: sleepCont."
invokeEvent p $ writeRef rh (Just h)
substituteCont :: MonadDES m => ContParams m a -> (a -> Event m ()) -> ContParams m a
substituteCont c m = c { contCont = m }
contAwait :: MonadDES m => Signal m a -> Cont m a
contAwait signal =
Cont $ \c ->
Event $ \p ->
do let r = pointRun p
c <- invokeEvent p $ freezeCont c
rh <- invokeSimulation r $ newRef Nothing
h <- invokeEvent p $
handleSignal signal $
\a -> Event $
\p -> do x <- invokeEvent p $ readRef rh
case x of
Nothing ->
error "The signal was lost: contAwait."
Just x ->
do invokeEvent p $ disposeEvent x
c <- invokeEvent p $ unfreezeCont c
case c of
Nothing -> return ()
Just c ->
invokeEvent p $ reenterCont c a
invokeEvent p $ writeRef rh $ Just h
transferCont :: MonadDES m => Cont m () -> Cont m a
transferCont x =
Cont $ \c ->
Event $ \p ->
do let worker =
do let cid = contId $ contAux c
cont = return
econt = throwEvent
ccont = return
when (contCatchFlag $ contAux c) $
error "Cannot be combined with the exception handling: unsafeTransferCont"
invokeEvent p $
runCont x cont econt ccont cid False
z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else worker
traceCont :: MonadDES m => String -> Cont m a -> Cont m a
traceCont message (Cont m) =
Cont $ \c ->
Event $ \p ->
do z <- invokeEvent p $ contCanceled c
if z
then invokeEvent p $ cancelCont c
else trace ("t = " ++ show (pointTime p) ++ ": " ++ message) $
invokeEvent p $ m c