aivika-0.4.1: examples/TimeOut.hs
-- It corresponds to model TimeOut described in document
-- Advanced Features of the SimPy Language
-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/AdvancedSimPy.pdf].
-- SimPy is available on [http://simpy.sourceforge.net/].
--
-- The model description is as follows.
--
-- Introductory example to illustrate the modeling of "competing
-- events" such as timeouts, especially using the cancelProcess function. A
-- network node sends a message but also sets a timeout period; if the
-- node times out, it assumes the message it had sent was lost, and it
-- will send again. The time to get an acknowledgement for a message is
-- exponentially distributed with mean 1.0, and the timeout period is
-- 0.5. Immediately after receiving an acknowledgement, the node sends
-- out a new message.
--
-- We find the proportion of messages which timeout. The output should
-- be about 0.61.
import System.Random
import Control.Monad
import Control.Monad.Trans
import Simulation.Aivika.Dynamics
import Simulation.Aivika.Dynamics.Simulation
import Simulation.Aivika.Dynamics.Base
import Simulation.Aivika.Dynamics.EventQueue
import Simulation.Aivika.Dynamics.Ref
import Simulation.Aivika.Dynamics.Process
ackRate = 1.0 / 1.0 -- reciprocal of the acknowledge mean time
toPeriod = 0.5 -- timeout period
specs = Specs { spcStartTime = 0.0,
spcStopTime = 10000.0,
spcDT = 1.0,
spcMethod = RungeKutta4 }
exprnd :: Double -> IO Double
exprnd lambda =
do x <- getStdRandom random
return (- log x / lambda)
model :: Simulation Double
model =
do queue <- newQueue
-- number of messages sent
nMsgs <- newRef queue 0
-- number of timeouts which have occured
nTimeOuts <- newRef queue 0
-- reactivatedCode will 1 if timeout occurred,
-- 2 ACK if received
reactivatedCode <- newRef queue 0
nodePid <- newProcessID queue
let node :: Process ()
node =
do liftDynamics $ modifyRef nMsgs $ (+) 1
-- create process IDs
timeoutPid <- liftSimulation $ newProcessID queue
ackPid <- liftSimulation $ newProcessID queue
-- set up the timeout
liftDynamics $ runProcessNow (timeout ackPid) timeoutPid
-- set up the message send/ACK
liftDynamics $ runProcessNow (acknowledge timeoutPid) ackPid
passivateProcess
code <- liftDynamics $ readRef reactivatedCode
when (code == 1) $
liftDynamics $ modifyRef nTimeOuts $ (+) 1
liftDynamics $ writeRef reactivatedCode 0
node
timeout :: ProcessID -> Process ()
timeout ackPid =
do holdProcess toPeriod
liftDynamics $
do writeRef reactivatedCode 1
reactivateProcess nodePid
cancelProcess ackPid
acknowledge :: ProcessID -> Process ()
acknowledge timeoutPid =
do ackTime <- liftIO $ exprnd ackRate
holdProcess ackTime
liftDynamics $
do writeRef reactivatedCode 2
reactivateProcess nodePid
cancelProcess timeoutPid
runDynamicsInStartTime $
runProcessNow node nodePid
runDynamicsInStopTime $
do x <- readRef nTimeOuts
y <- readRef nMsgs
return $ x / y
main =
do putStr "The percentage of timeout was "
runSimulation model specs >>= print