-- |
-- Module     : Simulation.Aivika.Resource.Preemption.Base
-- Copyright  : Copyright (c) 2009-2017, David Sorokin <david.sorokin@gmail.com>
-- License    : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- Stability  : experimental
-- Tested with: GHC 8.0.1
--
-- This module defines the preemptible resource.
--
-- The module is optimised in the sense that this kind of the resource
-- has neither additional signals, nor counters that would may slow
-- down the simulation.
--
module Simulation.Aivika.Resource.Preemption.Base
       (-- * Resource Type
        Resource,
        -- * Creating Resource
        newResource,
        newResourceWithMaxCount,
        -- * Resource Properties
        resourceMaxCount,
        resourceCount,
        -- * Requesting for and Releasing Resource
        requestResourceWithPriority,
        releaseResource,
        usingResourceWithPriority,
        -- * Altering Resource 
        incResourceCount,
        decResourceCount,
        alterResourceCount) where

import Data.IORef
import Data.Maybe

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.PriorityQueue as PQ

-- | Represents a preemptible resource.
data Resource = 
  Resource { Resource -> Maybe Int
resourceMaxCount :: Maybe Int,
             -- ^ Return the maximum count of the resource, where 'Nothing'
             -- means that the resource has no upper bound.
             Resource -> IORef Int
resourceCountRef :: IORef Int,
             Resource -> PriorityQueue ResourceActingItem
resourceActingQueue :: PQ.PriorityQueue ResourceActingItem,
             Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue :: PQ.PriorityQueue ResourceAwaitingItem }

-- | Identifies an acting item that acquired the resource.
data ResourceActingItem =
  ResourceActingItem { ResourceActingItem -> Double
actingItemPriority :: Double,
                       ResourceActingItem -> ProcessId
actingItemId :: ProcessId }

-- | Idenitifies an awaiting item that waits for releasing of the resource to take it.
type ResourceAwaitingItem = Either ResourceRequestingItem ResourcePreemptedItem

-- | Idenitifies an item that requests for the resource.
data ResourceRequestingItem =
  ResourceRequestingItem { ResourceRequestingItem -> Double
requestingItemPriority :: Double,
                           ResourceRequestingItem -> ProcessId
requestingItemId :: ProcessId,
                           ResourceRequestingItem -> FrozenCont ()
requestingItemCont :: FrozenCont () }

-- | Idenitifies an item that was preempted.
data ResourcePreemptedItem =
  ResourcePreemptedItem { ResourcePreemptedItem -> Double
preemptedItemPriority :: Double,
                          ResourcePreemptedItem -> ProcessId
preemptedItemId :: ProcessId }

instance Eq Resource where
  Resource
x == :: Resource -> Resource -> Bool
== Resource
y = Resource -> IORef Int
resourceCountRef Resource
x forall a. Eq a => a -> a -> Bool
== Resource -> IORef Int
resourceCountRef Resource
y  -- unique references

instance Eq ResourceActingItem where
  ResourceActingItem
x == :: ResourceActingItem -> ResourceActingItem -> Bool
== ResourceActingItem
y = ResourceActingItem -> ProcessId
actingItemId ResourceActingItem
x forall a. Eq a => a -> a -> Bool
== ResourceActingItem -> ProcessId
actingItemId ResourceActingItem
y

-- | Create a new resource with the specified initial count that becomes the upper bound as well.
newResource :: Int
               -- ^ the initial count (and maximal count too) of the resource
               -> Simulation Resource
newResource :: Int -> Simulation Resource
newResource Int
count =
  forall a. (Run -> IO a) -> Simulation a
Simulation forall a b. (a -> b) -> a -> b
$ \Run
r ->
  do forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
count forall a. Ord a => a -> a -> Bool
< Int
0) forall a b. (a -> b) -> a -> b
$
       forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
       String -> SimulationRetry
SimulationRetry forall a b. (a -> b) -> a -> b
$
       String
"The resource count cannot be negative: " forall a. [a] -> [a] -> [a]
++
       String
"newResource."
     IORef Int
countRef <- forall a. a -> IO (IORef a)
newIORef Int
count
     PriorityQueue ResourceActingItem
actingQueue <- forall a. IO (PriorityQueue a)
PQ.newQueue
     PriorityQueue ResourceAwaitingItem
waitQueue <- forall a. IO (PriorityQueue a)
PQ.newQueue
     forall (m :: * -> *) a. Monad m => a -> m a
return Resource { resourceMaxCount :: Maybe Int
resourceMaxCount = forall a. a -> Maybe a
Just Int
count,
                       resourceCountRef :: IORef Int
resourceCountRef = IORef Int
countRef,
                       resourceActingQueue :: PriorityQueue ResourceActingItem
resourceActingQueue = PriorityQueue ResourceActingItem
actingQueue,
                       resourceWaitQueue :: PriorityQueue ResourceAwaitingItem
resourceWaitQueue = PriorityQueue ResourceAwaitingItem
waitQueue }

-- | Create a new resource with the specified initial and maximum counts,
-- where 'Nothing' means that the resource has no upper bound.
newResourceWithMaxCount :: Int
                           -- ^ the initial count of the resource
                           -> Maybe Int
                           -- ^ the maximum count of the resource, which can be indefinite
                           -> Simulation Resource
newResourceWithMaxCount :: Int -> Maybe Int -> Simulation Resource
newResourceWithMaxCount Int
count Maybe Int
maxCount =
  forall a. (Run -> IO a) -> Simulation a
Simulation forall a b. (a -> b) -> a -> b
$ \Run
r ->
  do forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
count forall a. Ord a => a -> a -> Bool
< Int
0) forall a b. (a -> b) -> a -> b
$
       forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
       String -> SimulationRetry
SimulationRetry forall a b. (a -> b) -> a -> b
$
       String
"The resource count cannot be negative: " forall a. [a] -> [a] -> [a]
++
       String
"newResourceWithMaxCount."
     case Maybe Int
maxCount of
       Just Int
maxCount | Int
count forall a. Ord a => a -> a -> Bool
> Int
maxCount ->
         forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
         String -> SimulationRetry
SimulationRetry forall a b. (a -> b) -> a -> b
$
         String
"The resource count cannot be greater than " forall a. [a] -> [a] -> [a]
++
         String
"its maximum value: newResourceWithMaxCount."
       Maybe Int
_ ->
         forall (m :: * -> *) a. Monad m => a -> m a
return ()
     IORef Int
countRef <- forall a. a -> IO (IORef a)
newIORef Int
count
     PriorityQueue ResourceActingItem
actingQueue <- forall a. IO (PriorityQueue a)
PQ.newQueue
     PriorityQueue ResourceAwaitingItem
waitQueue <- forall a. IO (PriorityQueue a)
PQ.newQueue
     forall (m :: * -> *) a. Monad m => a -> m a
return Resource { resourceMaxCount :: Maybe Int
resourceMaxCount = Maybe Int
maxCount,
                       resourceCountRef :: IORef Int
resourceCountRef = IORef Int
countRef,
                       resourceActingQueue :: PriorityQueue ResourceActingItem
resourceActingQueue = PriorityQueue ResourceActingItem
actingQueue,
                       resourceWaitQueue :: PriorityQueue ResourceAwaitingItem
resourceWaitQueue = PriorityQueue ResourceAwaitingItem
waitQueue }

-- | Return the current count of the resource.
resourceCount :: Resource -> Event Int
resourceCount :: Resource -> Event Int
resourceCount Resource
r =
  forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p -> forall a. IORef a -> IO a
readIORef (Resource -> IORef Int
resourceCountRef Resource
r)

-- | Request with the priority for the resource decreasing its count
-- in case of success, otherwise suspending the discontinuous process
-- until some other process releases the resource.
--
-- It may preempt another process if the latter aquired the resource before
-- but had a lower priority. Then the current process takes an ownership of
-- the resource.
requestResourceWithPriority :: Resource
                               -- ^ the requested resource
                               -> Double
                               -- ^ the priority (the less value has a higher priority)
                               -> Process ()
requestResourceWithPriority :: Resource -> Double -> Process ()
requestResourceWithPriority Resource
r Double
priority =
  forall a. (ProcessId -> Cont a) -> Process a
Process forall a b. (a -> b) -> a -> b
$ \ProcessId
pid ->
  forall a. (ContParams a -> Event ()) -> Cont a
Cont forall a b. (a -> b) -> a -> b
$ \ContParams ()
c ->
  forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p ->
  do Int
a <- forall a. IORef a -> IO a
readIORef (Resource -> IORef Int
resourceCountRef Resource
r)
     if Int
a forall a. Eq a => a -> a -> Bool
== Int
0
       then do Bool
f <- forall a. PriorityQueue a -> IO Bool
PQ.queueNull (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
               if Bool
f
                 then do FrozenCont ()
c <- forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$
                              forall a. ContParams a -> a -> Event () -> Event (FrozenCont a)
freezeContReentering ContParams ()
c () forall a b. (a -> b) -> a -> b
$
                              forall a. ContParams a -> Cont a -> Event ()
invokeCont ContParams ()
c forall a b. (a -> b) -> a -> b
$
                              forall a. ProcessId -> Process a -> Cont a
invokeProcess ProcessId
pid forall a b. (a -> b) -> a -> b
$
                              Resource -> Double -> Process ()
requestResourceWithPriority Resource
r Double
priority
                         forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r) Double
priority (forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> FrozenCont () -> ResourceRequestingItem
ResourceRequestingItem Double
priority ProcessId
pid FrozenCont ()
c)
                 else do (Double
p0', ResourceActingItem
item0) <- forall a. PriorityQueue a -> IO (Double, a)
PQ.queueFront (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
                         let p0 :: Double
p0 = - Double
p0'
                             pid0 :: ProcessId
pid0 = ResourceActingItem -> ProcessId
actingItemId ResourceActingItem
item0
                         if Double
priority forall a. Ord a => a -> a -> Bool
< Double
p0
                           then do forall a. PriorityQueue a -> IO ()
PQ.dequeue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
                                   forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r) (- Double
priority) forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourceActingItem
ResourceActingItem Double
priority ProcessId
pid
                                   forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r) Double
p0 (forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourcePreemptedItem
ResourcePreemptedItem Double
p0 ProcessId
pid0)
                                   forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ ProcessId -> Event ()
processPreemptionBegin ProcessId
pid0
                                   forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. ContParams a -> a -> Event ()
resumeCont ContParams ()
c ()
                           else do FrozenCont ()
c <- forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$
                                        forall a. ContParams a -> a -> Event () -> Event (FrozenCont a)
freezeContReentering ContParams ()
c () forall a b. (a -> b) -> a -> b
$
                                        forall a. ContParams a -> Cont a -> Event ()
invokeCont ContParams ()
c forall a b. (a -> b) -> a -> b
$
                                        forall a. ProcessId -> Process a -> Cont a
invokeProcess ProcessId
pid forall a b. (a -> b) -> a -> b
$
                                        Resource -> Double -> Process ()
requestResourceWithPriority Resource
r Double
priority
                                   forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r) Double
priority (forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> FrozenCont () -> ResourceRequestingItem
ResourceRequestingItem Double
priority ProcessId
pid FrozenCont ()
c)
       else do let a' :: Int
a' = Int
a forall a. Num a => a -> a -> a
- Int
1
               Int
a' seq :: forall a b. a -> b -> b
`seq` forall a. IORef a -> a -> IO ()
writeIORef (Resource -> IORef Int
resourceCountRef Resource
r) Int
a'
               forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r) (- Double
priority) forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourceActingItem
ResourceActingItem Double
priority ProcessId
pid
               forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. ContParams a -> a -> Event ()
resumeCont ContParams ()
c ()

-- | Release the resource increasing its count and resuming one of the
-- previously suspended or preempted processes as possible.
releaseResource :: Resource
                   -- ^ the resource to release
                   -> Process ()
releaseResource :: Resource -> Process ()
releaseResource Resource
r = 
  forall a. (ProcessId -> Cont a) -> Process a
Process forall a b. (a -> b) -> a -> b
$ \ProcessId
pid ->
  forall a. (ContParams a -> Event ()) -> Cont a
Cont forall a b. (a -> b) -> a -> b
$ \ContParams ()
c ->
  forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p ->
  do Bool
f <- forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Maybe a -> Bool
isJust forall a b. (a -> b) -> a -> b
$ forall a. PriorityQueue a -> (a -> Bool) -> IO (Maybe a)
PQ.queueDeleteBy (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r) (\ResourceActingItem
item -> ResourceActingItem -> ProcessId
actingItemId ResourceActingItem
item forall a. Eq a => a -> a -> Bool
== ProcessId
pid)
     if Bool
f
       then do forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ Resource -> Event ()
releaseResource' Resource
r
               forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. ContParams a -> a -> Event ()
resumeCont ContParams ()
c ()
       else forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
            String -> SimulationRetry
SimulationRetry
            String
"The resource was not acquired by this process: releaseResource"

-- | Release the resource increasing its count and resuming one of the
-- previously suspended or preempted processes as possible.
releaseResource' :: Resource
                    -- ^ the resource to release
                    -> Event ()
releaseResource' :: Resource -> Event ()
releaseResource' Resource
r =
  forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p ->
  do Int
a <- forall a. IORef a -> IO a
readIORef (Resource -> IORef Int
resourceCountRef Resource
r)
     let a' :: Int
a' = Int
a forall a. Num a => a -> a -> a
+ Int
1
     case Resource -> Maybe Int
resourceMaxCount Resource
r of
       Just Int
maxCount | Int
a' forall a. Ord a => a -> a -> Bool
> Int
maxCount ->
         forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
         String -> SimulationRetry
SimulationRetry forall a b. (a -> b) -> a -> b
$
         String
"The resource count cannot be greater than " forall a. [a] -> [a] -> [a]
++
         String
"its maximum value: releaseResource'."
       Maybe Int
_ ->
         forall (m :: * -> *) a. Monad m => a -> m a
return ()
     Bool
f <- forall a. PriorityQueue a -> IO Bool
PQ.queueNull (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r)
     if Bool
f 
       then Int
a' seq :: forall a b. a -> b -> b
`seq` forall a. IORef a -> a -> IO ()
writeIORef (Resource -> IORef Int
resourceCountRef Resource
r) Int
a'
       else do (Double
priority', ResourceAwaitingItem
item) <- forall a. PriorityQueue a -> IO (Double, a)
PQ.queueFront (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r)
               forall a. PriorityQueue a -> IO ()
PQ.dequeue (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r)
               case ResourceAwaitingItem
item of
                 Left (ResourceRequestingItem Double
priority ProcessId
pid FrozenCont ()
c) ->
                   do Maybe (ContParams ())
c <- forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. FrozenCont a -> Event (Maybe (ContParams a))
unfreezeCont FrozenCont ()
c
                      case Maybe (ContParams ())
c of
                        Maybe (ContParams ())
Nothing ->
                          forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ Resource -> Event ()
releaseResource' Resource
r
                        Just ContParams ()
c ->
                          do forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r) (- Double
priority) forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourceActingItem
ResourceActingItem Double
priority ProcessId
pid
                             forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ Double -> Event () -> Event ()
enqueueEvent (Point -> Double
pointTime Point
p) forall a b. (a -> b) -> a -> b
$ forall a. ContParams a -> a -> Event ()
reenterCont ContParams ()
c ()
                 Right (ResourcePreemptedItem Double
priority ProcessId
pid) ->
                   do Bool
f <- forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ ProcessId -> Event Bool
processCancelled ProcessId
pid
                      case Bool
f of
                        Bool
True ->
                          forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ Resource -> Event ()
releaseResource' Resource
r
                        Bool
False ->
                          do forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r) (- Double
priority) forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourceActingItem
ResourceActingItem Double
priority ProcessId
pid
                             forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ ProcessId -> Event ()
processPreemptionEnd ProcessId
pid
               
-- | Acquire the resource with the specified priority, perform some action and
-- safely release the resource in the end, even if the 'IOException' was raised
-- within the action.
usingResourceWithPriority :: Resource
                             -- ^ the resource we are going to request for and then
                             -- release in the end
                             -> Double
                             -- ^ the priority (the less value has a higher priority)
                             -> Process a
                             -- ^ the action we are going to apply having the resource
                             -> Process a
                             -- ^ the result of the action
usingResourceWithPriority :: forall a. Resource -> Double -> Process a -> Process a
usingResourceWithPriority Resource
r Double
priority Process a
m =
  do Resource -> Double -> Process ()
requestResourceWithPriority Resource
r Double
priority
     forall a b. Process a -> Process b -> Process a
finallyProcess Process a
m forall a b. (a -> b) -> a -> b
$ Resource -> Process ()
releaseResource Resource
r

-- | Preempt a process with the lowest priority that acquires yet the resource
-- and decrease the count of available resource by 1. 
decResourceCount' :: Resource -> Event ()
decResourceCount' :: Resource -> Event ()
decResourceCount' Resource
r =
  forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p ->
  do Int
a <- forall a. IORef a -> IO a
readIORef (Resource -> IORef Int
resourceCountRef Resource
r)
     forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
a forall a. Eq a => a -> a -> Bool
== Int
0) forall a b. (a -> b) -> a -> b
$
       forall e a. Exception e => e -> IO a
throwIO forall a b. (a -> b) -> a -> b
$
       String -> SimulationRetry
SimulationRetry
       String
"The resource exceeded and its count is zero: decResourceCount'"
     Bool
f <- forall a. PriorityQueue a -> IO Bool
PQ.queueNull (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
     forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
f forall a b. (a -> b) -> a -> b
$
       do (Double
p0', ResourceActingItem
item0) <- forall a. PriorityQueue a -> IO (Double, a)
PQ.queueFront (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
          let p0 :: Double
p0 = - Double
p0'
              pid0 :: ProcessId
pid0 = ResourceActingItem -> ProcessId
actingItemId ResourceActingItem
item0
          forall a. PriorityQueue a -> IO ()
PQ.dequeue (Resource -> PriorityQueue ResourceActingItem
resourceActingQueue Resource
r)
          forall a. PriorityQueue a -> Double -> a -> IO ()
PQ.enqueue (Resource -> PriorityQueue ResourceAwaitingItem
resourceWaitQueue Resource
r) Double
p0 (forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Double -> ProcessId -> ResourcePreemptedItem
ResourcePreemptedItem Double
p0 ProcessId
pid0)
          forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ ProcessId -> Event ()
processPreemptionBegin ProcessId
pid0
     let a' :: Int
a' = Int
a forall a. Num a => a -> a -> a
- Int
1
     Int
a' seq :: forall a b. a -> b -> b
`seq` forall a. IORef a -> a -> IO ()
writeIORef (Resource -> IORef Int
resourceCountRef Resource
r) Int
a'

-- | Increase the count of available resource by the specified number,
-- invoking the awaiting and preempted processes according to their priorities
-- as needed.
incResourceCount :: Resource
                    -- ^ the resource
                    -> Int
                    -- ^ the increment for the resource count
                    -> Event ()
incResourceCount :: Resource -> Int -> Event ()
incResourceCount Resource
r Int
n
  | Int
n forall a. Ord a => a -> a -> Bool
< Int
0     = forall e a. Exception e => e -> Event a
throwEvent forall a b. (a -> b) -> a -> b
$ String -> SimulationRetry
SimulationRetry String
"The increment cannot be negative: incResourceCount"
  | Int
n forall a. Eq a => a -> a -> Bool
== Int
0    = forall (m :: * -> *) a. Monad m => a -> m a
return ()
  | Bool
otherwise =
    do Resource -> Event ()
releaseResource' Resource
r
       Resource -> Int -> Event ()
incResourceCount Resource
r (Int
n forall a. Num a => a -> a -> a
- Int
1)

-- | Decrease the count of available resource by the specified number,
-- preempting the processes according to their priorities as needed.
decResourceCount :: Resource
                    -- ^ the resource
                    -> Int
                    -- ^ the decrement for the resource count
                    -> Event ()
decResourceCount :: Resource -> Int -> Event ()
decResourceCount Resource
r Int
n
  | Int
n forall a. Ord a => a -> a -> Bool
< Int
0     = forall e a. Exception e => e -> Event a
throwEvent forall a b. (a -> b) -> a -> b
$ String -> SimulationRetry
SimulationRetry String
"The decrement cannot be negative: decResourceCount"
  | Int
n forall a. Eq a => a -> a -> Bool
== Int
0    = forall (m :: * -> *) a. Monad m => a -> m a
return ()
  | Bool
otherwise =
    do Resource -> Event ()
decResourceCount' Resource
r
       Resource -> Int -> Event ()
decResourceCount Resource
r (Int
n forall a. Num a => a -> a -> a
- Int
1)

-- | Alter the resource count either increasing or decreasing it by calling
-- 'incResourceCount' or 'decResourceCount' respectively. 
alterResourceCount :: Resource
                      -- ^ the resource
                      -> Int
                      -- ^ a change of the resource count
                      -> Event ()
alterResourceCount :: Resource -> Int -> Event ()
alterResourceCount Resource
r Int
n
  | Int
n forall a. Ord a => a -> a -> Bool
< Int
0  = Resource -> Int -> Event ()
decResourceCount Resource
r (- Int
n)
  | Int
n forall a. Ord a => a -> a -> Bool
> Int
0  = Resource -> Int -> Event ()
incResourceCount Resource
r Int
n
  | Int
n forall a. Eq a => a -> a -> Bool
== Int
0 = forall (m :: * -> *) a. Monad m => a -> m a
return ()