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