module Simulation.Aivika.Resource.Base
(
FCFSResource,
LCFSResource,
SIROResource,
PriorityResource,
Resource,
newFCFSResource,
newFCFSResourceWithMaxCount,
newLCFSResource,
newLCFSResourceWithMaxCount,
newSIROResource,
newSIROResourceWithMaxCount,
newPriorityResource,
newPriorityResourceWithMaxCount,
newResource,
newResourceWithMaxCount,
resourceStrategy,
resourceMaxCount,
resourceCount,
requestResource,
requestResourceWithPriority,
tryRequestResourceWithinEvent,
releaseResource,
releaseResourceWithinEvent,
usingResource,
usingResourceWithPriority,
incResourceCount,
decResourceCount) where
import Data.IORef
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 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,
resourceWaitList :: StrategyQueue s (FrozenCont ()) }
instance Eq (Resource s) where
x == y = resourceCountRef x == resourceCountRef y
newFCFSResource :: Int
-> Simulation FCFSResource
newFCFSResource = newResource FCFS
newFCFSResourceWithMaxCount :: Int
-> Maybe Int
-> Simulation FCFSResource
newFCFSResourceWithMaxCount = newResourceWithMaxCount FCFS
newLCFSResource :: Int
-> Simulation LCFSResource
newLCFSResource = newResource LCFS
newLCFSResourceWithMaxCount :: Int
-> Maybe Int
-> Simulation LCFSResource
newLCFSResourceWithMaxCount = newResourceWithMaxCount LCFS
newSIROResource :: Int
-> Simulation SIROResource
newSIROResource = newResource SIRO
newSIROResourceWithMaxCount :: Int
-> Maybe Int
-> Simulation SIROResource
newSIROResourceWithMaxCount = newResourceWithMaxCount SIRO
newPriorityResource :: Int
-> Simulation PriorityResource
newPriorityResource = newResource StaticPriorities
newPriorityResourceWithMaxCount :: Int
-> Maybe Int
-> Simulation PriorityResource
newPriorityResourceWithMaxCount = newResourceWithMaxCount StaticPriorities
newResource :: QueueStrategy s
=> s
-> Int
-> Simulation (Resource s)
newResource s count =
Simulation $ \r ->
do when (count < 0) $
throwIO $
SimulationRetry $
"The resource count cannot be negative: " ++
"newResource."
countRef <- newIORef count
waitList <- invokeSimulation r $ newStrategyQueue s
return Resource { resourceStrategy = s,
resourceMaxCount = Just count,
resourceCountRef = countRef,
resourceWaitList = waitList }
newResourceWithMaxCount :: QueueStrategy s
=> s
-> Int
-> Maybe Int
-> Simulation (Resource s)
newResourceWithMaxCount s count maxCount =
Simulation $ \r ->
do 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
waitList <- invokeSimulation r $ newStrategyQueue s
return Resource { resourceStrategy = s,
resourceMaxCount = maxCount,
resourceCountRef = countRef,
resourceWaitList = waitList }
resourceCount :: Resource s -> Event Int
resourceCount r =
Event $ \p -> readIORef (resourceCountRef 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) c
else do let a' = a 1
a' `seq` writeIORef (resourceCountRef r) a'
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 c
else do let a' = a 1
a' `seq` writeIORef (resourceCountRef r) a'
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 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: releaseResourceWithinEvent."
_ ->
return ()
f <- invokeEvent p $
strategyQueueNull (resourceWaitList r)
if f
then a' `seq` writeIORef (resourceCountRef r) a'
else do c <- invokeEvent p $
strategyDequeue (resourceWaitList r)
c <- invokeEvent p $ unfreezeCont c
case c of
Nothing ->
invokeEvent p $ releaseResourceWithinEvent r
Just c ->
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 let a' = a 1
a' `seq` writeIORef (resourceCountRef r) a'
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
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 releaseResourceWithinEvent 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 requestResource r
decResourceCount r (n 1)