{-# LANGUAGE FlexibleContexts #-}
module Simulation.Aivika.Trans.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 Control.Monad
import Control.Monad.Trans
import Control.Exception
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.DES
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.QueueStrategy
type FCFSResource m = Resource m FCFS
type LCFSResource m = Resource m LCFS
type SIROResource m = Resource m SIRO
type PriorityResource m = Resource m StaticPriorities
data Resource m s =
Resource { resourceStrategy :: s,
resourceMaxCount :: Maybe Int,
resourceCountRef :: Ref m Int,
resourceWaitList :: StrategyQueue m s (FrozenCont m ()) }
newFCFSResource :: MonadDES m
=> Int
-> Simulation m (FCFSResource m)
{-# INLINABLE newFCFSResource #-}
newFCFSResource = newResource FCFS
newFCFSResourceWithMaxCount :: MonadDES m
=> Int
-> Maybe Int
-> Simulation m (FCFSResource m)
{-# INLINABLE newFCFSResourceWithMaxCount #-}
newFCFSResourceWithMaxCount = newResourceWithMaxCount FCFS
newLCFSResource :: MonadDES m
=> Int
-> Simulation m (LCFSResource m)
{-# INLINABLE newLCFSResource #-}
newLCFSResource = newResource LCFS
newLCFSResourceWithMaxCount :: MonadDES m
=> Int
-> Maybe Int
-> Simulation m (LCFSResource m)
{-# INLINABLE newLCFSResourceWithMaxCount #-}
newLCFSResourceWithMaxCount = newResourceWithMaxCount LCFS
newSIROResource :: (MonadDES m, QueueStrategy m SIRO)
=> Int
-> Simulation m (SIROResource m)
{-# INLINABLE newSIROResource #-}
newSIROResource = newResource SIRO
newSIROResourceWithMaxCount :: (MonadDES m, QueueStrategy m SIRO)
=> Int
-> Maybe Int
-> Simulation m (SIROResource m)
{-# INLINABLE newSIROResourceWithMaxCount #-}
newSIROResourceWithMaxCount = newResourceWithMaxCount SIRO
newPriorityResource :: (MonadDES m, QueueStrategy m StaticPriorities)
=> Int
-> Simulation m (PriorityResource m)
{-# INLINABLE newPriorityResource #-}
newPriorityResource = newResource StaticPriorities
newPriorityResourceWithMaxCount :: (MonadDES m, QueueStrategy m StaticPriorities)
=> Int
-> Maybe Int
-> Simulation m (PriorityResource m)
{-# INLINABLE newPriorityResourceWithMaxCount #-}
newPriorityResourceWithMaxCount = newResourceWithMaxCount StaticPriorities
newResource :: (MonadDES m, QueueStrategy m s)
=> s
-> Int
-> Simulation m (Resource m s)
{-# INLINABLE newResource #-}
newResource s count =
Simulation $ \r ->
do when (count < 0) $
throwComp $
SimulationRetry $
"The resource count cannot be negative: " ++
"newResource."
countRef <- invokeSimulation r $ newRef count
waitList <- invokeSimulation r $ newStrategyQueue s
return Resource { resourceStrategy = s,
resourceMaxCount = Just count,
resourceCountRef = countRef,
resourceWaitList = waitList }
newResourceWithMaxCount :: (MonadDES m, QueueStrategy m s)
=> s
-> Int
-> Maybe Int
-> Simulation m (Resource m s)
{-# INLINABLE newResourceWithMaxCount #-}
newResourceWithMaxCount s count maxCount =
Simulation $ \r ->
do when (count < 0) $
throwComp $
SimulationRetry $
"The resource count cannot be negative: " ++
"newResourceWithMaxCount."
case maxCount of
Just maxCount | count > maxCount ->
throwComp $
SimulationRetry $
"The resource count cannot be greater than " ++
"its maximum value: newResourceWithMaxCount."
_ ->
return ()
countRef <- invokeSimulation r $ newRef count
waitList <- invokeSimulation r $ newStrategyQueue s
return Resource { resourceStrategy = s,
resourceMaxCount = maxCount,
resourceCountRef = countRef,
resourceWaitList = waitList }
resourceCount :: MonadDES m => Resource m s -> Event m Int
{-# INLINABLE resourceCount #-}
resourceCount r =
Event $ \p -> invokeEvent p $ readRef (resourceCountRef r)
requestResource :: (MonadDES m, EnqueueStrategy m s)
=> Resource m s
-> Process m ()
{-# INLINABLE requestResource #-}
requestResource r =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do a <- invokeEvent p $ readRef (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` invokeEvent p $ writeRef (resourceCountRef r) a'
invokeEvent p $ resumeCont c ()
requestResourceWithPriority :: (MonadDES m, PriorityQueueStrategy m s p)
=> Resource m s
-> p
-> Process m ()
{-# INLINABLE requestResourceWithPriority #-}
requestResourceWithPriority r priority =
Process $ \pid ->
Cont $ \c ->
Event $ \p ->
do a <- invokeEvent p $ readRef (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` invokeEvent p $ writeRef (resourceCountRef r) a'
invokeEvent p $ resumeCont c ()
releaseResource :: (MonadDES m, DequeueStrategy m s)
=> Resource m s
-> Process m ()
{-# INLINABLE releaseResource #-}
releaseResource r =
Process $ \_ ->
Cont $ \c ->
Event $ \p ->
do invokeEvent p $ releaseResourceWithinEvent r
invokeEvent p $ resumeCont c ()
releaseResourceWithinEvent :: (MonadDES m, DequeueStrategy m s)
=> Resource m s
-> Event m ()
{-# INLINABLE releaseResourceWithinEvent #-}
releaseResourceWithinEvent r =
Event $ \p ->
do a <- invokeEvent p $ readRef (resourceCountRef r)
let a' = a + 1
case resourceMaxCount r of
Just maxCount | a' > maxCount ->
throwComp $
SimulationRetry $
"The resource count cannot be greater than " ++
"its maximum value: releaseResourceWithinEvent."
_ ->
return ()
f <- invokeEvent p $
strategyQueueNull (resourceWaitList r)
if f
then a' `seq` invokeEvent p $ writeRef (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 :: MonadDES m
=> Resource m s
-> Event m Bool
{-# INLINABLE tryRequestResourceWithinEvent #-}
tryRequestResourceWithinEvent r =
Event $ \p ->
do a <- invokeEvent p $ readRef (resourceCountRef r)
if a == 0
then return False
else do let a' = a - 1
a' `seq` invokeEvent p $ writeRef (resourceCountRef r) a'
return True
usingResource :: (MonadDES m, EnqueueStrategy m s)
=> Resource m s
-> Process m a
-> Process m a
{-# INLINABLE usingResource #-}
usingResource r m =
do requestResource r
finallyProcess m $ releaseResource r
usingResourceWithPriority :: (MonadDES m, PriorityQueueStrategy m s p)
=> Resource m s
-> p
-> Process m a
-> Process m a
{-# INLINABLE usingResourceWithPriority #-}
usingResourceWithPriority r priority m =
do requestResourceWithPriority r priority
finallyProcess m $ releaseResource r
incResourceCount :: (MonadDES m, DequeueStrategy m s)
=> Resource m s
-> Int
-> Event m ()
{-# INLINABLE incResourceCount #-}
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 :: (MonadDES m, EnqueueStrategy m s)
=> Resource m s
-> Int
-> Process m ()
{-# INLINABLE decResourceCount #-}
decResourceCount r n
| n < 0 = throwProcess $ SimulationRetry "The decrement cannot be negative: decResourceCount"
| n == 0 = return ()
| otherwise =
do requestResource r
decResourceCount r (n - 1)