module Simulation.Aivika.IO.Event () where
import Control.Monad
import Control.Monad.Trans
import Data.IORef
import qualified Simulation.Aivika.PriorityQueue as PQ
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Comp
import Simulation.Aivika.Trans.Internal.Types
import Simulation.Aivika.Trans.Event
instance EventQueueing IO where
data EventQueue IO =
EventQueue { queuePQ :: PQ.PriorityQueue (Point IO -> IO ()),
queueBusy :: IORef Bool,
queueTime :: IORef Double
}
newEventQueue specs =
liftIO $
do f <- newIORef False
t <- newIORef $ spcStartTime specs
pq <- PQ.newQueue
return EventQueue { queuePQ = pq,
queueBusy = f,
queueTime = t }
enqueueEvent t (Event m) =
Event $ \p ->
let pq = queuePQ $ runEventQueue $ pointRun p
in liftIO $ PQ.enqueue pq t m
runEventWith processing (Event e) =
Dynamics $ \p ->
do invokeDynamics p $ processEvents processing
e p
eventQueueCount =
Event $
liftIO . PQ.queueCount . queuePQ . runEventQueue . pointRun
processPendingEventsCore :: Bool -> Dynamics IO ()
processPendingEventsCore includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
f = queueBusy q
f' <- liftIO $ readIORef f
unless f' $
do liftIO $ writeIORef f True
call q p
liftIO $ writeIORef f False
call q p =
do let pq = queuePQ q
r = pointRun p
f <- liftIO $ PQ.queueNull pq
unless f $
do (t2, c2) <- liftIO $ PQ.queueFront pq
let t = queueTime q
t' <- liftIO $ readIORef t
when (t2 < t') $
error "The time value is too small: processPendingEventsCore"
when ((t2 < pointTime p) ||
(includingCurrentEvents && (t2 == pointTime p))) $
do liftIO $ writeIORef t t2
liftIO $ 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 IO ()
processPendingEvents includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
t = queueTime q
t' <- liftIO $ 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 :: Dynamics IO ()
processEventsIncludingCurrent = processPendingEvents True
processEventsIncludingEarlier :: Dynamics IO ()
processEventsIncludingEarlier = processPendingEvents False
processEventsIncludingCurrentCore :: Dynamics IO ()
processEventsIncludingCurrentCore = processPendingEventsCore True
processEventsIncludingEarlierCore :: Dynamics IO ()
processEventsIncludingEarlierCore = processPendingEventsCore True
processEvents :: EventProcessing -> Dynamics IO ()
processEvents CurrentEvents = processEventsIncludingCurrent
processEvents EarlierEvents = processEventsIncludingEarlier
processEvents CurrentEventsOrFromPast = processEventsIncludingCurrentCore
processEvents EarlierEventsOrFromPast = processEventsIncludingEarlierCore
instance EventIOQueueing IO where
enqueueEventIO = enqueueEvent