module Simulation.Aivika.Internal.Cont
(ContCancellation(..),
ContCancellationSource,
Cont(..),
ContParams,
newContCancellationSource,
contCancellationInitiated,
contCancellationInitiate,
contCancellationInitiating,
contCancellationBind,
contCancellationConnect,
invokeCont,
runCont,
rerunCont,
spawnCont,
contParallel,
contParallel_,
catchCont,
finallyCont,
throwCont,
resumeCont,
resumeECont,
contCanceled,
contFreeze,
contAwait,
traceCont) where
import Data.IORef
import Data.Array
import Data.Array.IO.Safe
import Data.Monoid
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Applicative
import Debug.Trace
import Simulation.Aivika.Internal.Specs
import Simulation.Aivika.Internal.Parameter
import Simulation.Aivika.Internal.Simulation
import Simulation.Aivika.Internal.Dynamics
import Simulation.Aivika.Internal.Event
import Simulation.Aivika.Signal
data ContCancellation = CancelTogether
| CancelChildAfterParent
| CancelParentAfterChild
| CancelInIsolation
data ContCancellationSource =
ContCancellationSource { contCancellationInitiatedRef :: IORef Bool,
contCancellationActivatedRef :: IORef Bool,
contCancellationInitiatingSource :: SignalSource ()
}
newContCancellationSource :: Simulation ContCancellationSource
newContCancellationSource =
Simulation $ \r ->
do r1 <- newIORef False
r2 <- newIORef False
s <- invokeSimulation r newSignalSource
return ContCancellationSource { contCancellationInitiatedRef = r1,
contCancellationActivatedRef = r2,
contCancellationInitiatingSource = s
}
contCancellationInitiating :: ContCancellationSource -> Signal ()
contCancellationInitiating =
publishSignal . contCancellationInitiatingSource
contCancellationInitiated :: ContCancellationSource -> Event Bool
contCancellationInitiated x =
Event $ \p -> readIORef (contCancellationInitiatedRef x)
contCancellationActivated :: ContCancellationSource -> IO Bool
contCancellationActivated =
readIORef . contCancellationActivatedRef
contCancellationDeactivate :: ContCancellationSource -> IO ()
contCancellationDeactivate x =
writeIORef (contCancellationActivatedRef x) False
contCancellationBind :: ContCancellationSource -> [ContCancellationSource] -> Event DisposableEvent
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 :: ContCancellationSource
-> ContCancellation
-> ContCancellationSource
-> Event DisposableEvent
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 :: ContCancellationSource -> Event ()
contCancellationInitiate x =
Event $ \p ->
do f <- readIORef (contCancellationInitiatedRef x)
unless f $
do writeIORef (contCancellationInitiatedRef x) True
writeIORef (contCancellationActivatedRef x) True
invokeEvent p $ triggerSignal (contCancellationInitiatingSource x) ()
newtype Cont a = Cont (ContParams a -> Event ())
data ContParams a =
ContParams { contCont :: a -> Event (),
contAux :: ContParamsAux }
data ContParamsAux =
ContParamsAux { contECont :: SomeException -> Event (),
contCCont :: () -> Event (),
contCancelSource :: ContCancellationSource,
contCancelFlag :: IO Bool,
contCatchFlag :: Bool }
instance Monad Cont where
return = returnC
m >>= k = bindC m k
instance ParameterLift Cont where
liftParameter = liftPC
instance SimulationLift Cont where
liftSimulation = liftSC
instance DynamicsLift Cont where
liftDynamics = liftDC
instance EventLift Cont where
liftEvent = liftEC
instance Functor Cont where
fmap = liftM
instance Applicative Cont where
pure = return
(<*>) = ap
instance MonadIO Cont where
liftIO = liftIOC
invokeCont :: ContParams a -> Cont a -> Event ()
invokeCont p (Cont m) = m p
cancelCont :: Point -> ContParams a -> IO ()
cancelCont p c =
do contCancellationDeactivate (contCancelSource $ contAux c)
invokeEvent p $ (contCCont $ contAux c) ()
returnC :: a -> Cont a
returnC a =
Cont $ \c ->
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ contCont c a
bindC :: Cont a -> (a -> Cont b) -> Cont b
bindC (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 }
callCont :: (a -> Cont b) -> a -> ContParams b -> Event ()
callCont k a c =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ invokeCont c (k a)
catchCont :: Exception e => Cont a -> (e -> Cont a) -> Cont 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 :: Cont a -> Cont b -> Cont 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 :: IOException -> Cont a
throwCont = liftIO . throw
runCont :: Cont a
-> (a -> Event ())
-> (SomeException -> Event ())
-> (() -> Event ())
-> ContCancellationSource
-> Bool
-> Event ()
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 } }
liftPC :: Parameter a -> Cont a
liftPC (Parameter m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftIOWithCatch (m $ pointRun p) p c
else liftIOWithoutCatch (m $ pointRun p) p c
liftSC :: Simulation a -> Cont a
liftSC (Simulation m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftIOWithCatch (m $ pointRun p) p c
else liftIOWithoutCatch (m $ pointRun p) p c
liftDC :: Dynamics a -> Cont a
liftDC (Dynamics m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftIOWithCatch (m p) p c
else liftIOWithoutCatch (m p) p c
liftEC :: Event a -> Cont a
liftEC (Event m) =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftIOWithCatch (m p) p c
else liftIOWithoutCatch (m p) p c
liftIOC :: IO a -> Cont a
liftIOC m =
Cont $ \c ->
Event $ \p ->
if contCatchFlag . contAux $ c
then liftIOWithCatch m p c
else liftIOWithoutCatch m p c
liftIOWithoutCatch :: IO a -> Point -> ContParams a -> IO ()
liftIOWithoutCatch m p c =
do z <- contCanceled c
if z
then cancelCont p c
else do a <- m
invokeEvent p $ contCont c a
liftIOWithCatch :: IO a -> Point -> ContParams a -> IO ()
liftIOWithCatch m p c =
do z <- contCanceled c
if z
then cancelCont p c
else do aref <- newIORef undefined
eref <- newIORef Nothing
catch (m >>= writeIORef aref)
(writeIORef eref . Just)
e <- readIORef eref
case e of
Nothing ->
do a <- readIORef aref
invokeEvent p $ contCont c a
Just e ->
invokeEvent p $ (contECont . contAux) c e
resumeCont :: ContParams a -> a -> Event ()
resumeCont c a =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ contCont c a
resumeECont :: ContParams a -> SomeException -> Event ()
resumeECont c e =
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else invokeEvent p $ (contECont $ contAux c) e
contCanceled :: ContParams a -> IO Bool
contCanceled c = contCancelFlag $ contAux c
contParallel :: [(Cont a, ContCancellationSource)]
-> Cont [a]
contParallel xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
worker =
do results <- newArray_ (1, n) :: IO (IOArray Int a)
counter <- newIORef 0
catchRef <- newIORef Nothing
hs <- invokeEvent p $
contCancellationBind (contCancelSource $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- readIORef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- contCanceled c
f2 <- readIORef catchRef
case (f1, f2) of
(False, Nothing) ->
do rs <- getElems results
invokeEvent p $ resumeCont c rs
(False, Just e) ->
invokeEvent p $ resumeECont c e
(True, _) ->
cancelCont p c
cont i a =
Event $ \p ->
do modifyIORef counter (+ 1)
writeArray results i a
invokeEvent p propagate
econt e =
Event $ \p ->
do modifyIORef counter (+ 1)
r <- readIORef catchRef
case r of
Nothing -> writeIORef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do modifyIORef counter (+ 1)
invokeEvent p propagate
forM_ (zip [1..n] 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_ :: [(Cont a, ContCancellationSource)]
-> Cont ()
contParallel_ xs =
Cont $ \c ->
Event $ \p ->
do let n = length xs
worker =
do counter <- newIORef 0
catchRef <- newIORef Nothing
hs <- invokeEvent p $
contCancellationBind (contCancelSource $ contAux c) $
map snd xs
let propagate =
Event $ \p ->
do n' <- readIORef counter
when (n' == n) $
do invokeEvent p $ disposeEvent hs
f1 <- contCanceled c
f2 <- readIORef 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 modifyIORef counter (+ 1)
invokeEvent p propagate
econt e =
Event $ \p ->
do modifyIORef counter (+ 1)
r <- readIORef catchRef
case r of
Nothing -> writeIORef catchRef $ Just e
Just e' -> return ()
invokeEvent p propagate
ccont e =
Event $ \p ->
do modifyIORef counter (+ 1)
invokeEvent p propagate
forM_ (zip [1..n] 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 :: Cont a -> ContCancellationSource -> Cont 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 :: ContCancellation -> Cont () -> ContCancellationSource -> Cont ()
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 :: ContParams a -> Event (Event (Maybe (ContParams a)))
contFreeze c =
Event $ \p ->
do rh <- newIORef Nothing
rc <- newIORef $ Just c
h <- invokeEvent p $
handleSignal (contCancellationInitiating $
contCancelSource $
contAux c) $ \a ->
Event $ \p ->
do h <- readIORef rh
case h of
Nothing ->
error "The handler was lost: contFreeze."
Just h ->
do invokeEvent p $ disposeEvent h
c <- readIORef rc
case c of
Nothing -> return ()
Just c ->
do writeIORef rc Nothing
invokeEvent p $
enqueueEvent (pointTime p) $
Event $ \p ->
do z <- contCanceled c
when z $ cancelCont p c
writeIORef rh (Just h)
return $
Event $ \p ->
do invokeEvent p $ disposeEvent h
c <- readIORef rc
writeIORef rc Nothing
return c
contAwait :: Signal a -> Cont a
contAwait signal =
Cont $ \c ->
Event $ \p ->
do c <- invokeEvent p $ contFreeze c
r <- newIORef Nothing
h <- invokeEvent p $
handleSignal signal $
\a -> Event $
\p -> do x <- readIORef 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
writeIORef r $ Just h
traceCont :: String -> Cont a -> Cont a
traceCont message (Cont m) =
Cont $ \c ->
Event $ \p ->
do z <- contCanceled c
if z
then cancelCont p c
else trace ("t = " ++ show (pointTime p) ++ ": " ++ message) $
invokeEvent p $ m c