module Simulation.Aivika.Trans.GPSS.Transact
(Transact,
transactValue,
transactArrivalDelay,
transactArrivalTime,
transactPriority,
transactAssemblySet,
newTransact,
splitTransact,
assignTransactValue,
assignTransactValueM,
assignTransactPriority,
takeTransact,
releaseTransact,
transactPreemptionBegin,
transactPreemptionEnd,
requireTransactProcessId,
transferTransact,
reactivateTransacts,
registerTransactQueueEntry,
unregisterTransactQueueEntry) where
import Control.Monad
import Control.Monad.Trans
import Control.Exception
import qualified Data.HashMap.Lazy as HM
import Simulation.Aivika.Trans
import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Internal.Cont
import Simulation.Aivika.Trans.Internal.Process
import Simulation.Aivika.Trans.GPSS.Queue
import Simulation.Aivika.Trans.GPSS.AssemblySet
data Transact m a =
Transact { transactValue :: a,
transactArrivalDelay :: Maybe Double,
transactArrivalTime :: Double,
transactPriority :: Int,
transactAssemblySetRef :: Ref m (Maybe (AssemblySet m)),
transactPreemptionCountRef :: Ref m Int,
transactProcessIdRef :: Ref m (Maybe (ProcessId m)),
transactProcessContRef :: Ref m (Maybe (FrozenCont m ())),
transactQueueEntryRef :: Ref m (HM.HashMap (Queue m) (QueueEntry m))
}
instance MonadDES m => Eq (Transact m a) where
x == y = (transactProcessIdRef x) == (transactProcessIdRef y)
newTransact :: MonadDES m
=> Arrival a
-> Int
-> Simulation m (Transact m a)
newTransact a priority =
Simulation $ \r ->
do r0 <- invokeSimulation r $ newRef 0
r1 <- invokeSimulation r $ newRef Nothing
r2 <- invokeSimulation r $ newRef Nothing
r3 <- invokeSimulation r $ newRef HM.empty
r4 <- invokeSimulation r $ newRef Nothing
return Transact { transactValue = arrivalValue a,
transactArrivalDelay = arrivalDelay a,
transactArrivalTime = arrivalTime a,
transactPriority = priority,
transactAssemblySetRef = r4,
transactPreemptionCountRef = r0,
transactProcessIdRef = r1,
transactProcessContRef = r2,
transactQueueEntryRef = r3
}
splitTransact :: MonadDES m => Transact m a -> Simulation m (Transact m a)
splitTransact t =
Simulation $ \r ->
do r0 <- invokeSimulation r $ newRef 0
r1 <- invokeSimulation r $ newRef Nothing
r2 <- invokeSimulation r $ newRef Nothing
r3 <- invokeSimulation r $ newRef HM.empty
return Transact { transactValue = transactValue t,
transactArrivalDelay = transactArrivalDelay t,
transactArrivalTime = transactArrivalTime t,
transactPriority = transactPriority t,
transactAssemblySetRef = transactAssemblySetRef t,
transactPreemptionCountRef = r0,
transactProcessIdRef = r1,
transactProcessContRef = r2,
transactQueueEntryRef = r3
}
transactAssemblySet :: MonadDES m => Transact m a -> Event m (AssemblySet m)
transactAssemblySet t =
Event $ \p ->
do let r = pointRun p
x <- invokeEvent p $ readRef (transactAssemblySetRef t)
case x of
Just a -> return a
Nothing ->
do a <- invokeSimulation r newAssemblySet
invokeEvent p $ writeRef (transactAssemblySetRef t) (Just a)
return a
takeTransact :: MonadDES m => Transact m a -> Process m ()
takeTransact t =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do pid0 <- invokeEvent p $ readRef (transactProcessIdRef t)
case pid0 of
Just pid0 ->
throwComp $
SimulationRetry
"The transact is acquired by another process: takeTransact"
Nothing ->
do invokeEvent p $ writeRef (transactProcessIdRef t) (Just pid)
n <- invokeEvent p $ readRef (transactPreemptionCountRef t)
if n == 0
then invokeEvent p $ resumeCont c ()
else do c <- invokeEvent p $
freezeContReentering c () $
invokeCont c $
invokeProcess pid $
takeTransact t
invokeEvent p $
writeRef (transactProcessContRef t) (Just c)
forM_ [1 .. n] $ \_ ->
invokeEvent p $
processPreemptionBegin pid
releaseTransact :: MonadDES m => Transact m a -> Process m ()
releaseTransact t =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do pid0 <- invokeEvent p $ readRef (transactProcessIdRef t)
case pid0 of
Nothing ->
throwComp $
SimulationRetry
"The transact is not acquired by any process: releaseTransact"
Just pid0 | pid0 /= pid ->
throwComp $
SimulationRetry
"The transact is acquired by another process: releaseTransact"
Just pid0 ->
do invokeEvent p $ writeRef (transactProcessIdRef t) Nothing
invokeEvent p $ writeRef (transactProcessContRef t) Nothing
invokeEvent p $ resumeCont c ()
transactPreemptionBegin :: MonadDES m => Transact m a -> Event m ()
transactPreemptionBegin t =
Event $ \p ->
do n <- invokeEvent p $ readRef (transactPreemptionCountRef t)
let n' = n + 1
n' `seq` invokeEvent p $ writeRef (transactPreemptionCountRef t) n'
pid <- invokeEvent p $ readRef (transactProcessIdRef t)
case pid of
Nothing -> return ()
Just pid -> invokeEvent p $ processPreemptionBegin pid
transactPreemptionEnd :: MonadDES m => Transact m a -> Event m ()
transactPreemptionEnd t =
Event $ \p ->
do n <- invokeEvent p $ readRef (transactPreemptionCountRef t)
let n' = n 1
unless (n' >= 0) $
throwComp $
SimulationRetry
"The transact preemption count cannot be negative: transactPreemptionEnd"
n' `seq` invokeEvent p $ writeRef (transactPreemptionCountRef t) n'
pid <- invokeEvent p $ readRef (transactProcessIdRef t)
case pid of
Nothing -> return ()
Just pid ->
do invokeEvent p $ processPreemptionEnd pid
c <- invokeEvent p $ readRef (transactProcessContRef t)
case c of
Nothing -> return ()
Just c ->
do invokeEvent p $ writeRef (transactProcessContRef t) Nothing
c <- invokeEvent p $ unfreezeCont c
case c of
Nothing -> return ()
Just c -> invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()
requireTransactProcessId :: MonadDES m => Transact m a -> Event m (ProcessId m)
requireTransactProcessId t =
Event $ \p ->
do a <- invokeEvent p $ readRef (transactProcessIdRef t)
case a of
Nothing ->
throwComp $
SimulationRetry
"The transact must be associated with any process: requireTransactProcessId"
Just pid ->
return pid
transferTransact :: MonadDES m => Transact m a -> Process m () -> Event m ()
transferTransact t transfer =
Event $ \p ->
do a <- invokeEvent p $ readRef (transactProcessIdRef t)
case a of
Nothing -> return ()
Just pid ->
invokeEvent p $ cancelProcessWithId pid
invokeEvent p $ writeRef (transactProcessIdRef t) Nothing
invokeEvent p $ writeRef (transactProcessContRef t) Nothing
invokeEvent p $
runProcess $
do takeTransact t
transferProcess transfer
registerTransactQueueEntry :: MonadDES m => Transact m a -> QueueEntry m -> Event m ()
registerTransactQueueEntry t e =
Event $ \p ->
do let q = entryQueue e
m <- invokeEvent p $ readRef (transactQueueEntryRef t)
case HM.lookup q m of
Just e0 ->
throwComp $
SimulationRetry
"There is already another queue entry for the specified queue: registerTransactQueueEntry"
Nothing ->
invokeEvent p $ writeRef (transactQueueEntryRef t) (HM.insert q e m)
unregisterTransactQueueEntry :: MonadDES m => Transact m a -> Queue m -> Event m (QueueEntry m)
unregisterTransactQueueEntry t q =
Event $ \p ->
do m <- invokeEvent p $ readRef (transactQueueEntryRef t)
case HM.lookup q m of
Nothing ->
throwComp $
SimulationRetry
"There is no queue entry for the specified queue: unregisterTransactQueueEntry"
Just e ->
do invokeEvent p $ writeRef (transactQueueEntryRef t) (HM.delete q m)
return e
assignTransactValue :: Transact m a -> (a -> b) -> Transact m b
assignTransactValue t f =
let b = f (transactValue t)
in t { transactValue = b }
assignTransactValueM :: Monad c => Transact m a -> (a -> c b) -> c (Transact m b)
assignTransactValueM t f =
do b <- f (transactValue t)
return t { transactValue = b }
assignTransactPriority :: Transact m a -> Int -> Transact m a
assignTransactPriority t priority =
t { transactPriority = priority }
reactivateTransacts :: MonadDES m => [(Transact m a, Maybe (Process m ()))] -> Event m ()
reactivateTransacts [] = return ()
reactivateTransacts ((t, Nothing): xs) =
do pid <- requireTransactProcessId t
reactivateProcess pid
reactivateTransacts xs
reactivateTransacts ((t, Just transfer): xs) =
do transferTransact t transfer
reactivateTransacts xs