{-# LANGUAGE RecursiveDo, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, MonoLocalBinds, RankNTypes #-}
module Simulation.Aivika.Trans.Internal.Event
(
Event(..),
EventLift(..),
EventProcessing(..),
invokeEvent,
runEventInStartTime,
runEventInStopTime,
EventQueueing(..),
enqueueEventWithCancellation,
enqueueEventWithStartTime,
enqueueEventWithStopTime,
enqueueEventWithTimes,
enqueueEventWithPoints,
enqueueEventWithIntegTimes,
yieldEvent,
EventCancellation,
cancelEvent,
eventCancelled,
eventFinished,
catchEvent,
finallyEvent,
throwEvent,
memoEvent,
memoEventInTime,
DisposableEvent(..),
retryEvent,
EventIOQueueing(..),
enqueueEventIOWithStartTime,
enqueueEventIOWithStopTime,
enqueueEventIOWithTimes,
enqueueEventIOWithPoints,
enqueueEventIOWithIntegTimes,
traceEvent) where
import Data.Monoid hiding ((<>))
import Data.Semigroup (Semigroup(..))
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Fix
import Control.Monad.Fail
import qualified Control.Monad.Catch as MC
import Control.Applicative
import Debug.Trace (trace)
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.Comp
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Internal.Types
import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Internal.Parameter
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Dynamics
instance Monad m => Monad (Event m) where
{-# INLINE return #-}
return a = Event $ \p -> return a
{-# INLINE (>>=) #-}
(Event m) >>= k =
Event $ \p ->
do a <- m p
let Event m' = k a
m' p
instance Functor m => Functor (Event m) where
{-# INLINE fmap #-}
fmap f (Event x) = Event $ \p -> fmap f $ x p
instance Applicative m => Applicative (Event m) where
{-# INLINE pure #-}
pure = Event . const . pure
{-# INLINE (<*>) #-}
(Event x) <*> (Event y) = Event $ \p -> x p <*> y p
instance Monad m => MonadFail (Event m) where
{-# INLINE fail #-}
fail = error
instance MonadTrans Event where
{-# INLINE lift #-}
lift = Event . const
instance MonadIO m => MonadIO (Event m) where
{-# INLINE liftIO #-}
liftIO = Event . const . liftIO
instance Monad m => MonadCompTrans Event m where
{-# INLINE liftComp #-}
liftComp = Event . const
class EventLift t m where
liftEvent :: Event m a -> t m a
instance Monad m => EventLift Event m where
{-# INLINE liftEvent #-}
liftEvent = id
instance Monad m => DynamicsLift Event m where
{-# INLINE liftDynamics #-}
liftDynamics (Dynamics x) = Event x
instance Monad m => SimulationLift Event m where
{-# INLINE liftSimulation #-}
liftSimulation (Simulation x) = Event $ x . pointRun
instance Monad m => ParameterLift Event m where
{-# INLINE liftParameter #-}
liftParameter (Parameter x) = Event $ x . pointRun
catchEvent :: (MonadException m, Exception e) => Event m a -> (e -> Event m a) -> Event m a
{-# INLINABLE catchEvent #-}
catchEvent (Event m) h =
Event $ \p ->
catchComp (m p) $ \e ->
let Event m' = h e in m' p
finallyEvent :: MonadException m => Event m a -> Event m b -> Event m a
{-# INLINABLE finallyEvent #-}
finallyEvent (Event m) (Event m') =
Event $ \p ->
finallyComp (m p) (m' p)
throwEvent :: (MonadException m, Exception e) => e -> Event m a
{-# INLINABLE throwEvent #-}
throwEvent e =
Event $ \p ->
throwComp e
maskEvent :: MC.MonadMask m => ((forall a. Event m a -> Event m a) -> Event m b) -> Event m b
{-# INLINABLE maskEvent #-}
maskEvent a =
Event $ \p ->
MC.mask $ \u ->
invokeEvent p (a $ q u)
where q u (Event b) = Event (u . b)
uninterruptibleMaskEvent :: MC.MonadMask m => ((forall a. Event m a -> Event m a) -> Event m b) -> Event m b
{-# INLINABLE uninterruptibleMaskEvent #-}
uninterruptibleMaskEvent a =
Event $ \p ->
MC.uninterruptibleMask $ \u ->
invokeEvent p (a $ q u)
where q u (Event b) = Event (u . b)
generalBracketEvent :: MC.MonadMask m
=> Event m a
-> (a -> MC.ExitCase b -> Event m c)
-> (a -> Event m b)
-> Event m (b, c)
{-# INLINABLE generalBracketEvent #-}
generalBracketEvent acquire release use =
Event $ \p -> do
MC.generalBracket
(invokeEvent p acquire)
(\resource e -> invokeEvent p $ release resource e)
(\resource -> invokeEvent p $ use resource)
instance MonadFix m => MonadFix (Event m) where
{-# INLINE mfix #-}
mfix f =
Event $ \p ->
do { rec { a <- invokeEvent p (f a) }; return a }
instance MonadException m => MC.MonadThrow (Event m) where
{-# INLINE throwM #-}
throwM = throwEvent
instance MonadException m => MC.MonadCatch (Event m) where
{-# INLINE catch #-}
catch = catchEvent
instance (MonadException m, MC.MonadMask m) => MC.MonadMask (Event m) where
{-# INLINE mask #-}
mask = maskEvent
{-# INLINE uninterruptibleMask #-}
uninterruptibleMask = uninterruptibleMaskEvent
{-# INLINE generalBracket #-}
generalBracket = generalBracketEvent
runEventInStartTime :: MonadDES m => Event m a -> Simulation m a
{-# INLINE runEventInStartTime #-}
runEventInStartTime = runDynamicsInStartTime . runEvent
runEventInStopTime :: MonadDES m => Event m a -> Simulation m a
{-# INLINE runEventInStopTime #-}
runEventInStopTime = runDynamicsInStopTime . runEvent
enqueueEventWithTimes :: MonadDES m => [Double] -> Event m () -> Event m ()
{-# INLINABLE enqueueEventWithTimes #-}
enqueueEventWithTimes ts e = loop ts
where loop [] = return ()
loop (t : ts) = enqueueEvent t $ e >> loop ts
enqueueEventWithPoints :: MonadDES m => [Point m] -> Event m () -> Event m ()
{-# INLINABLE enqueueEventWithPoints #-}
enqueueEventWithPoints xs (Event e) = loop xs
where loop [] = return ()
loop (x : xs) = enqueueEvent (pointTime x) $
Event $ \p ->
do e x
invokeEvent p $ loop xs
enqueueEventWithIntegTimes :: MonadDES m => Event m () -> Event m ()
{-# INLINABLE enqueueEventWithIntegTimes #-}
enqueueEventWithIntegTimes e =
Event $ \p ->
let points = integPointsStartingFrom p
in invokeEvent p $ enqueueEventWithPoints points e
enqueueEventWithStartTime :: MonadDES m => Event m () -> Event m ()
{-# INLINABLE enqueueEventWithStartTime #-}
enqueueEventWithStartTime e =
Event $ \p ->
let p0 = integStartPoint $ pointRun p
in invokeEvent p $ enqueueEventWithPoints [p0] e
enqueueEventWithStopTime :: MonadDES m => Event m () -> Event m ()
{-# INLINABLE enqueueEventWithStopTime #-}
enqueueEventWithStopTime e =
Event $ \p ->
let p0 = simulationStopPoint $ pointRun p
in invokeEvent p $ enqueueEventWithPoints [p0] e
data EventCancellation m =
EventCancellation { cancelEvent :: Event m (),
eventCancelled :: Event m Bool,
eventFinished :: Event m Bool
}
enqueueEventWithCancellation :: MonadDES m => Double -> Event m () -> Event m (EventCancellation m)
{-# INLINABLE enqueueEventWithCancellation #-}
enqueueEventWithCancellation t e =
Event $ \p ->
do let r = pointRun p
cancelledRef <- invokeSimulation r $ newRef False
cancellableRef <- invokeSimulation r $ newRef True
finishedRef <- invokeSimulation r $ newRef False
let cancel =
Event $ \p ->
do x <- invokeEvent p $ readRef cancellableRef
when x $
invokeEvent p $ writeRef cancelledRef True
cancelled =
readRef cancelledRef
finished =
readRef finishedRef
invokeEvent p $
enqueueEvent t $
Event $ \p ->
do invokeEvent p $ writeRef cancellableRef False
x <- invokeEvent p $ readRef cancelledRef
unless x $
do invokeEvent p e
invokeEvent p $ writeRef finishedRef True
return EventCancellation { cancelEvent = cancel,
eventCancelled = cancelled,
eventFinished = finished }
memoEvent :: MonadDES m => Event m a -> Simulation m (Event m a)
{-# INLINABLE memoEvent #-}
memoEvent m =
Simulation $ \r ->
do ref <- invokeSimulation r $ newRef Nothing
return $ Event $ \p ->
do x <- invokeEvent p $ readRef ref
case x of
Just v -> return v
Nothing ->
do v <- invokeEvent p m
invokeEvent p $ writeRef ref (Just v)
return v
memoEventInTime :: MonadDES m => Event m a -> Simulation m (Event m a)
{-# INLINABLE memoEventInTime #-}
memoEventInTime m =
Simulation $ \r ->
do ref <- invokeSimulation r $ newRef Nothing
return $ Event $ \p ->
do x <- invokeEvent p $ readRef ref
case x of
Just (t, v) | t == pointTime p ->
return v
_ ->
do v <- invokeEvent p m
invokeEvent p $ writeRef ref (Just (pointTime p, v))
return v
yieldEvent :: MonadDES m => Event m () -> Event m ()
{-# INLINABLE yieldEvent #-}
yieldEvent m =
Event $ \p ->
invokeEvent p $
enqueueEvent (pointTime p) m
newtype DisposableEvent m =
DisposableEvent { disposeEvent :: Event m ()
}
instance Monad m => Semigroup (DisposableEvent m) where
{-# INLINE (<>) #-}
DisposableEvent x <> DisposableEvent y = DisposableEvent $ x >> y
instance Monad m => Monoid (DisposableEvent m) where
{-# INLINE mempty #-}
mempty = DisposableEvent $ return ()
{-# INLINE mappend #-}
mappend = (<>)
retryEvent :: MonadException m => String -> Event m a
retryEvent message = throwEvent $ SimulationRetry message
traceEvent :: MonadDES m => String -> Event m a -> Event m a
{-# INLINABLE traceEvent #-}
traceEvent message m =
Event $ \p ->
trace ("t = " ++ show (pointTime p) ++ ": " ++ message) $
invokeEvent p m
class (EventQueueing m, MonadIO (Event m)) => EventIOQueueing m where
enqueueEventIO :: Double -> Event m () -> Event m ()
enqueueEventIOWithTimes :: (MonadDES m, EventIOQueueing m) => [Double] -> Event m () -> Event m ()
{-# INLINABLE enqueueEventIOWithTimes #-}
enqueueEventIOWithTimes ts e = loop ts
where loop [] = return ()
loop (t : ts) = enqueueEventIO t $ e >> loop ts
enqueueEventIOWithPoints :: (MonadDES m, EventIOQueueing m) => [Point m] -> Event m () -> Event m ()
{-# INLINABLE enqueueEventIOWithPoints #-}
enqueueEventIOWithPoints xs (Event e) = loop xs
where loop [] = return ()
loop (x : xs) = enqueueEventIO (pointTime x) $
Event $ \p ->
do e x
invokeEvent p $ loop xs
enqueueEventIOWithIntegTimes :: (MonadDES m, EventIOQueueing m) => Event m () -> Event m ()
{-# INLINABLE enqueueEventIOWithIntegTimes #-}
enqueueEventIOWithIntegTimes e =
Event $ \p ->
let points = integPointsStartingFrom p
in invokeEvent p $ enqueueEventIOWithPoints points e
enqueueEventIOWithStartTime :: (MonadDES m, EventIOQueueing m) => Event m () -> Event m ()
{-# INLINABLE enqueueEventIOWithStartTime #-}
enqueueEventIOWithStartTime e =
Event $ \p ->
let p0 = integStartPoint $ pointRun p
in invokeEvent p $ enqueueEventIOWithPoints [p0] e
enqueueEventIOWithStopTime :: (MonadDES m, EventIOQueueing m) => Event m () -> Event m ()
{-# INLINABLE enqueueEventIOWithStopTime #-}
enqueueEventIOWithStopTime e =
Event $ \p ->
let p0 = simulationStopPoint $ pointRun p
in invokeEvent p $ enqueueEventIOWithPoints [p0] e