module Simulation.Aivika.Internal.Event
(
Event(..),
EventLift(..),
EventProcessing(..),
invokeEvent,
runEvent,
runEventWith,
runEventInStartTime,
runEventInStopTime,
enqueueEvent,
enqueueEventWithCancellation,
enqueueEventWithTimes,
enqueueEventWithPoints,
enqueueEventWithIntegTimes,
yieldEvent,
eventQueueCount,
EventCancellation,
cancelEvent,
eventCancelled,
eventFinished,
catchEvent,
finallyEvent,
throwEvent,
memoEvent,
memoEventInTime,
DisposableEvent(..)) where
import Data.IORef
import Data.Monoid
import qualified Control.Exception as C
import Control.Exception (IOException, throw, finally)
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Fix
import Control.Applicative
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
returnE a = Event (\p -> return a)
bindE :: Event a -> (a -> Event b) -> Event b
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
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
liftPS (Parameter m) =
Event $ \p -> m $ pointRun p
liftES :: Simulation a -> Event a
liftES (Simulation m) =
Event $ \p -> m $ pointRun p
liftDS :: Dynamics a -> Event a
liftDS (Dynamics m) =
Event m
class EventLift m where
liftEvent :: Event a -> m a
instance EventLift Event where
liftEvent = id
catchEvent :: Event a -> (IOException -> Event a) -> Event a
catchEvent (Event m) h =
Event $ \p ->
C.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 ->
C.finally (m p) (m' p)
throwEvent :: IOException -> Event a
throwEvent = throw
invokeEvent :: Point -> Event a -> IO a
invokeEvent p (Event m) = m p
instance MonadFix Event where
mfix f =
Event $ \p ->
do { rec { a <- invokeEvent p (f a) }; return a }
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 = integPoints $ pointRun p
in invokeEvent p $ enqueueEventWithPoints points 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 Monoid DisposableEvent where
mempty = DisposableEvent $ return ()
mappend (DisposableEvent x) (DisposableEvent y) = DisposableEvent $ x >> y