{-# LANGUAGE RecursiveDo, RankNTypes #-}
module Simulation.Aivika.Internal.Event
(
Event(..),
EventLift(..),
EventProcessing(..),
invokeEvent,
runEvent,
runEventWith,
runEventInStartTime,
runEventInStopTime,
enqueueEvent,
enqueueEventWithCancellation,
enqueueEventWithStartTime,
enqueueEventWithStopTime,
enqueueEventWithTimes,
enqueueEventWithPoints,
enqueueEventWithIntegTimes,
yieldEvent,
eventQueueCount,
EventCancellation,
cancelEvent,
eventCancelled,
eventFinished,
catchEvent,
finallyEvent,
throwEvent,
memoEvent,
memoEventInTime,
DisposableEvent(..),
retryEvent,
traceEvent) where
import Data.IORef
import Data.Monoid hiding ((<>))
import Data.Semigroup (Semigroup(..))
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Fix
import qualified Control.Monad.Catch as MC
import Control.Applicative
import Debug.Trace (trace)
import qualified Simulation.Aivika.PriorityQueue as PQ
import Simulation.Aivika.Internal.Specs
import Simulation.Aivika.Internal.Parameter
import Simulation.Aivika.Internal.Simulation
import Simulation.Aivika.Internal.Dynamics
newtype Event a = Event (Point -> IO a)
instance Monad Event where
return = returnE
m >>= k = bindE m k
returnE :: a -> Event a
{-# INLINE returnE #-}
returnE a = Event (\p -> return a)
bindE :: Event a -> (a -> Event b) -> Event b
{-# INLINE bindE #-}
bindE (Event m) k =
Event $ \p ->
do a <- m p
let Event m' = k a
m' p
instance Functor Event where
fmap = liftME
instance Applicative Event where
pure = return
(<*>) = ap
liftME :: (a -> b) -> Event a -> Event b
{-# INLINE liftME #-}
liftME f (Event x) =
Event $ \p -> do { a <- x p; return $ f a }
instance MonadIO Event where
liftIO m = Event $ const m
instance ParameterLift Event where
liftParameter = liftPS
instance SimulationLift Event where
liftSimulation = liftES
instance DynamicsLift Event where
liftDynamics = liftDS
liftPS :: Parameter a -> Event a
{-# INLINE liftPS #-}
liftPS (Parameter m) =
Event $ \p -> m $ pointRun p
liftES :: Simulation a -> Event a
{-# INLINE liftES #-}
liftES (Simulation m) =
Event $ \p -> m $ pointRun p
liftDS :: Dynamics a -> Event a
{-# INLINE liftDS #-}
liftDS (Dynamics m) =
Event m
class EventLift m where
liftEvent :: Event a -> m a
instance EventLift Event where
liftEvent = id
catchEvent :: Exception e => Event a -> (e -> Event a) -> Event a
catchEvent (Event m) h =
Event $ \p ->
catch (m p) $ \e ->
let Event m' = h e in m' p
finallyEvent :: Event a -> Event b -> Event a
finallyEvent (Event m) (Event m') =
Event $ \p ->
finally (m p) (m' p)
throwEvent :: Exception e => e -> Event a
throwEvent = throw
maskEvent :: ((forall a. Event a -> Event a) -> Event b) -> Event b
maskEvent a =
Event $ \p ->
MC.mask $ \u ->
invokeEvent p (a $ q u)
where q u (Event b) = Event (u . b)
uninterruptibleMaskEvent :: ((forall a. Event a -> Event a) -> Event b) -> Event b
uninterruptibleMaskEvent a =
Event $ \p ->
MC.uninterruptibleMask $ \u ->
invokeEvent p (a $ q u)
where q u (Event b) = Event (u . b)
invokeEvent :: Point -> Event a -> IO a
{-# INLINE invokeEvent #-}
invokeEvent p (Event m) = m p
instance MonadFix Event where
mfix f =
Event $ \p ->
do { rec { a <- invokeEvent p (f a) }; return a }
instance MC.MonadThrow Event where
throwM = throwEvent
instance MC.MonadCatch Event where
catch = catchEvent
instance MC.MonadMask Event where
mask = maskEvent
uninterruptibleMask = uninterruptibleMaskEvent
data EventProcessing = CurrentEvents
| EarlierEvents
| CurrentEventsOrFromPast
| EarlierEventsOrFromPast
deriving (Eq, Ord, Show)
enqueueEvent :: Double -> Event () -> Event ()
enqueueEvent t (Event m) =
Event $ \p ->
let pq = queuePQ $ runEventQueue $ pointRun p
in PQ.enqueue pq t m
processPendingEventsCore :: Bool -> Dynamics ()
processPendingEventsCore includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
f = queueBusy q
f' <- readIORef f
unless f' $
do writeIORef f True
call q p
writeIORef f False
call q p =
do let pq = queuePQ q
r = pointRun p
f <- PQ.queueNull pq
unless f $
do (t2, c2) <- PQ.queueFront pq
let t = queueTime q
t' <- readIORef t
when (t2 < t') $
error "The time value is too small: processPendingEventsCore"
when ((t2 < pointTime p) ||
(includingCurrentEvents && (t2 == pointTime p))) $
do writeIORef t t2
PQ.dequeue pq
let sc = pointSpecs p
t0 = spcStartTime sc
dt = spcDT sc
n2 = fromIntegral $ floor ((t2 - t0) / dt)
c2 $ p { pointTime = t2,
pointIteration = n2,
pointPhase = -1 }
call q p
processPendingEvents :: Bool -> Dynamics ()
processPendingEvents includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
t = queueTime q
t' <- readIORef t
if pointTime p < t'
then error $
"The current time is less than " ++
"the time in the queue: processPendingEvents"
else invokeDynamics p m
m = processPendingEventsCore includingCurrentEvents
processEventsIncludingCurrent = processPendingEvents True
processEventsIncludingEarlier = processPendingEvents False
processEventsIncludingCurrentCore = processPendingEventsCore True
processEventsIncludingEarlierCore = processPendingEventsCore True
processEvents :: EventProcessing -> Dynamics ()
processEvents CurrentEvents = processEventsIncludingCurrent
processEvents EarlierEvents = processEventsIncludingEarlier
processEvents CurrentEventsOrFromPast = processEventsIncludingCurrentCore
processEvents EarlierEventsOrFromPast = processEventsIncludingEarlierCore
runEvent :: Event a -> Dynamics a
runEvent = runEventWith CurrentEvents
runEventWith :: EventProcessing -> Event a -> Dynamics a
runEventWith processing (Event e) =
Dynamics $ \p ->
do invokeDynamics p $ processEvents processing
e p
runEventInStartTime :: Event a -> Simulation a
runEventInStartTime = runDynamicsInStartTime . runEvent
runEventInStopTime :: Event a -> Simulation a
runEventInStopTime = runDynamicsInStopTime . runEvent
eventQueueCount :: Event Int
eventQueueCount =
Event $ PQ.queueCount . queuePQ . runEventQueue . pointRun
enqueueEventWithTimes :: [Double] -> Event () -> Event ()
enqueueEventWithTimes ts e = loop ts
where loop [] = return ()
loop (t : ts) = enqueueEvent t $ e >> loop ts
enqueueEventWithPoints :: [Point] -> Event () -> Event ()
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 :: Event () -> Event ()
enqueueEventWithIntegTimes e =
Event $ \p ->
let points = integPointsStartingFrom p
in invokeEvent p $ enqueueEventWithPoints points e
enqueueEventWithStartTime :: Event () -> Event ()
enqueueEventWithStartTime e =
Event $ \p ->
let p0 = integStartPoint $ pointRun p
in invokeEvent p $ enqueueEventWithPoints [p0] e
enqueueEventWithStopTime :: Event () -> Event ()
enqueueEventWithStopTime e =
Event $ \p ->
let p0 = simulationStopPoint $ pointRun p
in invokeEvent p $ enqueueEventWithPoints [p0] e
data EventCancellation =
EventCancellation { cancelEvent :: Event (),
eventCancelled :: Event Bool,
eventFinished :: Event Bool
}
enqueueEventWithCancellation :: Double -> Event () -> Event EventCancellation
enqueueEventWithCancellation t e =
Event $ \p ->
do cancelledRef <- newIORef False
cancellableRef <- newIORef True
finishedRef <- newIORef False
let cancel =
Event $ \p ->
do x <- readIORef cancellableRef
when x $
writeIORef cancelledRef True
cancelled =
Event $ \p -> readIORef cancelledRef
finished =
Event $ \p -> readIORef finishedRef
invokeEvent p $
enqueueEvent t $
Event $ \p ->
do writeIORef cancellableRef False
x <- readIORef cancelledRef
unless x $
do invokeEvent p e
writeIORef finishedRef True
return EventCancellation { cancelEvent = cancel,
eventCancelled = cancelled,
eventFinished = finished }
memoEvent :: Event a -> Simulation (Event a)
memoEvent m =
do ref <- liftIO $ newIORef Nothing
return $ Event $ \p ->
do x <- readIORef ref
case x of
Just v -> return v
Nothing ->
do v <- invokeEvent p m
writeIORef ref (Just v)
return v
memoEventInTime :: Event a -> Simulation (Event a)
memoEventInTime m =
do ref <- liftIO $ newIORef Nothing
return $ Event $ \p ->
do x <- readIORef ref
case x of
Just (t, v) | t == pointTime p ->
return v
_ ->
do v <- invokeEvent p m
writeIORef ref (Just (pointTime p, v))
return v
yieldEvent :: Event () -> Event ()
yieldEvent m =
Event $ \p ->
invokeEvent p $
enqueueEvent (pointTime p) m
newtype DisposableEvent =
DisposableEvent { disposeEvent :: Event ()
}
instance Semigroup DisposableEvent where
DisposableEvent x <> DisposableEvent y = DisposableEvent $ x >> y
instance Monoid DisposableEvent where
mempty = DisposableEvent $ return ()
mappend = (<>)
retryEvent :: String -> Event a
retryEvent message = throwEvent $ SimulationRetry message
traceEvent :: String -> Event a -> Event a
traceEvent message m =
Event $ \p ->
trace ("t = " ++ show (pointTime p) ++ ": " ++ message) $
invokeEvent p m