module Simulation.Aivika.Resource
(
FCFSResource,
LCFSResource,
SIROResource,
PriorityResource,
Resource,
newFCFSResource,
newFCFSResourceWithMaxCount,
newLCFSResource,
newLCFSResourceWithMaxCount,
newSIROResource,
newSIROResourceWithMaxCount,
newPriorityResource,
newPriorityResourceWithMaxCount,
newResource,
newResourceWithMaxCount,
resourceStrategy,
resourceMaxCount,
resourceCount,
resourceCountStats,
resourceUtilisationCount,
resourceUtilisationCountStats,
resourceQueueCount,
resourceQueueCountStats,
resourceTotalWaitTime,
resourceWaitTime,
requestResource,
requestResourceWithPriority,
tryRequestResourceWithinEvent,
releaseResource,
releaseResourceWithinEvent,
usingResource,
usingResourceWithPriority,
incResourceCount,
decResourceCount,
resetResource,
resourceCountChanged,
resourceCountChanged_,
resourceUtilisationCountChanged,
resourceUtilisationCountChanged_,
resourceQueueCountChanged,
resourceQueueCountChanged_,
resourceWaitTimeChanged,
resourceWaitTimeChanged_,
resourceChanged_) where
import Data.IORef
import Data.Monoid
import Control.Monad
import Control.Monad.Trans
import Control.Exception
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.QueueStrategy
import Simulation.Aivika.Statistics
import Simulation.Aivika.Signal
import qualified Simulation.Aivika.DoubleLinkedList as DLL
import qualified Simulation.Aivika.Vector as V
import qualified Simulation.Aivika.PriorityQueue as PQ
type FCFSResource = Resource FCFS
type LCFSResource = Resource LCFS
type SIROResource = Resource SIRO
type PriorityResource = Resource StaticPriorities
data Resource s =
Resource { resourceStrategy :: s,
resourceMaxCount :: Maybe Int,
resourceCountRef :: IORef Int,
resourceCountStatsRef :: IORef (TimingStats Int),
resourceCountSource :: SignalSource Int,
resourceUtilisationCountRef :: IORef Int,
resourceUtilisationCountStatsRef :: IORef (TimingStats Int),
resourceUtilisationCountSource :: SignalSource Int,
resourceQueueCountRef :: IORef Int,
resourceQueueCountStatsRef :: IORef (TimingStats Int),
resourceQueueCountSource :: SignalSource Int,
resourceTotalWaitTimeRef :: IORef Double,
resourceWaitTimeRef :: IORef (SamplingStats Double),
resourceWaitTimeSource :: SignalSource (),
resourceWaitList :: StrategyQueue s ResourceItem }
data ResourceItem =
ResourceItem { resourceItemTime :: Double,
resourceItemCont :: FrozenCont () }
instance Eq (Resource s) where
x == y = resourceCountRef x == resourceCountRef y
newFCFSResource :: Int
-> Event FCFSResource
newFCFSResource = newResource FCFS
newFCFSResourceWithMaxCount :: Int
-> Maybe Int
-> Event FCFSResource
newFCFSResourceWithMaxCount = newResourceWithMaxCount FCFS
newLCFSResource :: Int
-> Event LCFSResource
newLCFSResource = newResource LCFS
newLCFSResourceWithMaxCount :: Int
-> Maybe Int
-> Event LCFSResource
newLCFSResourceWithMaxCount = newResourceWithMaxCount LCFS
newSIROResource :: Int
-> Event SIROResource
newSIROResource = newResource SIRO
newSIROResourceWithMaxCount :: Int
-> Maybe Int
-> Event SIROResource
newSIROResourceWithMaxCount = newResourceWithMaxCount SIRO
newPriorityResource :: Int
-> Event PriorityResource
newPriorityResource = newResource StaticPriorities
newPriorityResourceWithMaxCount :: Int
-> Maybe Int
-> Event PriorityResource
newPriorityResourceWithMaxCount = newResourceWithMaxCount StaticPriorities
newResource :: QueueStrategy s
=> s
-> Int
-> Event (Resource s)
newResource s count =
newResourceWithMaxCount s count (Just count)
newResourceWithMaxCount :: QueueStrategy s
=> s
-> Int
-> Maybe Int
-> Event (Resource s)
newResourceWithMaxCount s count maxCount =
Event $ \p ->
do let r = pointRun p
t = pointTime p
when (count < 0) $
throwIO $
SimulationRetry $
"The resource count cannot be negative: " ++
"newResourceWithMaxCount."
case maxCount of
Just maxCount | count > maxCount ->
throwIO $
SimulationRetry $
"The resource count cannot be greater than " ++
"its maximum value: newResourceWithMaxCount."
_ ->
return ()
countRef <- newIORef count
countStatsRef <- newIORef $ returnTimingStats t count
countSource <- invokeSimulation r newSignalSource
utilCountRef <- newIORef 0
utilCountStatsRef <- newIORef $ returnTimingStats t 0
utilCountSource <- invokeSimulation r newSignalSource
queueCountRef <- newIORef 0
queueCountStatsRef <- newIORef $ returnTimingStats t 0
queueCountSource <- invokeSimulation r newSignalSource
totalWaitTimeRef <- newIORef 0
waitTimeRef <- newIORef emptySamplingStats
waitTimeSource <- invokeSimulation r newSignalSource
waitList <- invokeSimulation r $ newStrategyQueue s
return Resource { resourceStrategy = s,
resourceMaxCount = maxCount,
resourceCountRef = countRef,
resourceCountStatsRef = countStatsRef,
resourceCountSource = countSource,
resourceUtilisationCountRef = utilCountRef,
resourceUtilisationCountStatsRef = utilCountStatsRef,
resourceUtilisationCountSource = utilCountSource,
resourceQueueCountRef = queueCountRef,
resourceQueueCountStatsRef = queueCountStatsRef,
resourceQueueCountSource = queueCountSource,
resourceTotalWaitTimeRef = totalWaitTimeRef,
resourceWaitTimeRef = waitTimeRef,
resourceWaitTimeSource = waitTimeSource,
resourceWaitList = waitList }
resourceCount :: Resource s -> Event Int
resourceCount r =
Event $ \p -> readIORef (resourceCountRef r)
resourceCountStats :: Resource s -> Event (TimingStats Int)
resourceCountStats r =
Event $ \p -> readIORef (resourceCountStatsRef r)
resourceCountChanged :: Resource s -> Signal Int
resourceCountChanged r =
publishSignal $ resourceCountSource r
resourceCountChanged_ :: Resource s -> Signal ()
resourceCountChanged_ r =
mapSignal (const ()) $ resourceCountChanged r
resourceUtilisationCount :: Resource s -> Event Int
resourceUtilisationCount r =
Event $ \p -> readIORef (resourceUtilisationCountRef r)
resourceUtilisationCountStats :: Resource s -> Event (TimingStats Int)
resourceUtilisationCountStats r =
Event $ \p -> readIORef (resourceUtilisationCountStatsRef r)
resourceUtilisationCountChanged :: Resource s -> Signal Int
resourceUtilisationCountChanged r =
publishSignal $ resourceUtilisationCountSource r
resourceUtilisationCountChanged_ :: Resource s -> Signal ()
resourceUtilisationCountChanged_ r =
mapSignal (const ()) $ resourceUtilisationCountChanged r
resourceQueueCount :: Resource s -> Event Int
resourceQueueCount r =
Event $ \p -> readIORef (resourceQueueCountRef r)
resourceQueueCountStats :: Resource s -> Event (TimingStats Int)
resourceQueueCountStats r =
Event $ \p -> readIORef (resourceQueueCountStatsRef r)
resourceQueueCountChanged :: Resource s -> Signal Int
resourceQueueCountChanged r =
publishSignal $ resourceQueueCountSource r
resourceQueueCountChanged_ :: Resource s -> Signal ()
resourceQueueCountChanged_ r =
mapSignal (const ()) $ resourceQueueCountChanged r
resourceTotalWaitTime :: Resource s -> Event Double
resourceTotalWaitTime r =
Event $ \p -> readIORef (resourceTotalWaitTimeRef r)
resourceWaitTime :: Resource s -> Event (SamplingStats Double)
resourceWaitTime r =
Event $ \p -> readIORef (resourceWaitTimeRef r)
resourceWaitTimeChanged :: Resource s -> Signal (SamplingStats Double)
resourceWaitTimeChanged r =
mapSignalM (\() -> resourceWaitTime r) $ resourceWaitTimeChanged_ r
resourceWaitTimeChanged_ :: Resource s -> Signal ()
resourceWaitTimeChanged_ r =
publishSignal $ resourceWaitTimeSource r
requestResource :: EnqueueStrategy s
=> Resource s
-> Process ()
requestResource r =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do a <- readIORef (resourceCountRef r)
if a == 0
then do c <- invokeEvent p $
freezeContReentering c () $
invokeCont c $
invokeProcess pid $
requestResource r
invokeEvent p $
strategyEnqueue (resourceWaitList r) $
ResourceItem (pointTime p) c
invokeEvent p $ updateResourceQueueCount r 1
else do invokeEvent p $ updateResourceWaitTime r 0
invokeEvent p $ updateResourceCount r (-1)
invokeEvent p $ updateResourceUtilisationCount r 1
invokeEvent p $ resumeCont c ()
requestResourceWithPriority :: PriorityQueueStrategy s p
=> Resource s
-> p
-> Process ()
requestResourceWithPriority r priority =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do a <- readIORef (resourceCountRef r)
if a == 0
then do c <- invokeEvent p $
freezeContReentering c () $
invokeCont c $
invokeProcess pid $
requestResourceWithPriority r priority
invokeEvent p $
strategyEnqueueWithPriority (resourceWaitList r) priority $
ResourceItem (pointTime p) c
invokeEvent p $ updateResourceQueueCount r 1
else do invokeEvent p $ updateResourceWaitTime r 0
invokeEvent p $ updateResourceCount r (-1)
invokeEvent p $ updateResourceUtilisationCount r 1
invokeEvent p $ resumeCont c ()
releaseResource :: DequeueStrategy s
=> Resource s
-> Process ()
releaseResource r =
Process $ \_ ->
Cont $ \c ->
Event $ \p ->
do invokeEvent p $ releaseResourceWithinEvent r
invokeEvent p $ resumeCont c ()
releaseResourceWithinEvent :: DequeueStrategy s
=> Resource s
-> Event ()
releaseResourceWithinEvent r =
Event $ \p ->
do invokeEvent p $ updateResourceUtilisationCount r (-1)
invokeEvent p $ releaseResource' r
releaseResource' :: DequeueStrategy s
=> Resource s
-> Event ()
releaseResource' r =
Event $ \p ->
do a <- readIORef (resourceCountRef r)
let a' = a + 1
case resourceMaxCount r of
Just maxCount | a' > maxCount ->
throwIO $
SimulationRetry $
"The resource count cannot be greater than " ++
"its maximum value: releaseResource'."
_ ->
return ()
f <- invokeEvent p $
strategyQueueNull (resourceWaitList r)
if f
then invokeEvent p $ updateResourceCount r 1
else do x <- invokeEvent p $
strategyDequeue (resourceWaitList r)
invokeEvent p $ updateResourceQueueCount r (-1)
c <- invokeEvent p $ unfreezeCont (resourceItemCont x)
case c of
Nothing ->
invokeEvent p $ releaseResource' r
Just c ->
do invokeEvent p $ updateResourceWaitTime r (pointTime p - resourceItemTime x)
invokeEvent p $ updateResourceUtilisationCount r 1
invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()
tryRequestResourceWithinEvent :: Resource s
-> Event Bool
tryRequestResourceWithinEvent r =
Event $ \p ->
do a <- readIORef (resourceCountRef r)
if a == 0
then return False
else do invokeEvent p $ updateResourceWaitTime r 0
invokeEvent p $ updateResourceCount r (-1)
invokeEvent p $ updateResourceUtilisationCount r 1
return True
usingResource :: EnqueueStrategy s
=> Resource s
-> Process a
-> Process a
usingResource r m =
do requestResource r
finallyProcess m $ releaseResource r
usingResourceWithPriority :: PriorityQueueStrategy s p
=> Resource s
-> p
-> Process a
-> Process a
usingResourceWithPriority r priority m =
do requestResourceWithPriority r priority
finallyProcess m $ releaseResource r
decResourceCount' :: EnqueueStrategy s
=> Resource s
-> Process ()
decResourceCount' r =
do liftEvent $
updateResourceUtilisationCount r (-1)
requestResource r
incResourceCount :: DequeueStrategy s
=> Resource s
-> Int
-> Event ()
incResourceCount r n
| n < 0 = throwEvent $ SimulationRetry "The increment cannot be negative: incResourceCount"
| n == 0 = return ()
| otherwise =
do releaseResource' r
incResourceCount r (n - 1)
decResourceCount :: EnqueueStrategy s
=> Resource s
-> Int
-> Process ()
decResourceCount r n
| n < 0 = throwProcess $ SimulationRetry "The decrement cannot be negative: decResourceCount"
| n == 0 = return ()
| otherwise =
do decResourceCount' r
decResourceCount r (n - 1)
resourceChanged_ :: Resource s -> Signal ()
resourceChanged_ r =
resourceCountChanged_ r <>
resourceUtilisationCountChanged_ r <>
resourceQueueCountChanged_ r
updateResourceCount :: Resource s -> Int -> Event ()
updateResourceCount r delta =
Event $ \p ->
do a <- readIORef (resourceCountRef r)
let a' = a + delta
a' `seq` writeIORef (resourceCountRef r) a'
modifyIORef' (resourceCountStatsRef r) $
addTimingStats (pointTime p) a'
invokeEvent p $
triggerSignal (resourceCountSource r) a'
updateResourceUtilisationCount :: Resource s -> Int -> Event ()
updateResourceUtilisationCount r delta =
Event $ \p ->
do a <- readIORef (resourceUtilisationCountRef r)
let a' = a + delta
a' `seq` writeIORef (resourceUtilisationCountRef r) a'
modifyIORef' (resourceUtilisationCountStatsRef r) $
addTimingStats (pointTime p) a'
invokeEvent p $
triggerSignal (resourceUtilisationCountSource r) a'
updateResourceQueueCount :: Resource s -> Int -> Event ()
updateResourceQueueCount r delta =
Event $ \p ->
do a <- readIORef (resourceQueueCountRef r)
let a' = a + delta
a' `seq` writeIORef (resourceQueueCountRef r) a'
modifyIORef' (resourceQueueCountStatsRef r) $
addTimingStats (pointTime p) a'
invokeEvent p $
triggerSignal (resourceQueueCountSource r) a'
updateResourceWaitTime :: Resource s -> Double -> Event ()
updateResourceWaitTime r delta =
Event $ \p ->
do a <- readIORef (resourceTotalWaitTimeRef r)
let a' = a + delta
a' `seq` writeIORef (resourceTotalWaitTimeRef r) a'
modifyIORef' (resourceWaitTimeRef r) $
addSamplingStats delta
invokeEvent p $
triggerSignal (resourceWaitTimeSource r) ()
resetResource :: Resource s -> Event ()
resetResource r =
Event $ \p ->
do let t = pointTime p
count <- readIORef (resourceCountRef r)
writeIORef (resourceCountStatsRef r) $
returnTimingStats t count
utilCount <- readIORef (resourceUtilisationCountRef r)
writeIORef (resourceUtilisationCountStatsRef r) $
returnTimingStats t utilCount
queueCount <- readIORef (resourceQueueCountRef r)
writeIORef (resourceQueueCountStatsRef r) $
returnTimingStats t queueCount
writeIORef (resourceTotalWaitTimeRef r) 0
writeIORef (resourceWaitTimeRef r) emptySamplingStats
invokeEvent p $
triggerSignal (resourceWaitTimeSource r) ()