SafeSemaphore-0.9.0: src/Control/Concurrent/STM/SSem.hs
-----------------------------------------------------------------------------
-- |
-- Module : Control.Concurrent.STM.SSem
-- Copyright : (c) Chris Kuklewicz, 2012
-- License : BSD-style
--
-- Maintainer : haskell@list.mightyreason.com
-- Stability : experimental
-- Portability : non-portable (concurrency)
--
-- Very simple quantity semaphore.
--
-----------------------------------------------------------------------------
module Control.Concurrent.STM.SSem(SSem, new, wait, signal, tryWait
, waitN, signalN, tryWaitN
, getValue) where
import Control.Monad.STM(STM,retry)
import Control.Concurrent.STM.TVar(newTVar,readTVar,writeTVar)
import Control.Concurrent.STM.SSemInternals(SSem(SSem))
-- | Create a new semaphore with the given argument as the initially available quantity. This
-- allows new semaphores to start with a negative, zero, or positive quantity.
new :: Int -> STM SSem
new = fmap SSem . newTVar
-- | Try to take a unit of value from the semaphore. This succeeds when the current quantity is
-- positive, and then reduces the quantity by one. Otherwise this will 'retry'. This will never
-- result in a negative quantity. If several threads are retying then which one succeeds next is
-- undefined -- an unlucky thread might starve.
wait :: SSem -> STM ()
wait = flip waitN 1
-- | Try to take the given value from the semaphore. This succeeds when the quantity is greater or
-- equal to the given value, and then subtracts the given value from the quantity. Otherwise this
-- will 'retry'. This will never result in a negative quantity. If several threads are retrying
-- then which one succeeds next is undefined -- an unlucky thread might starve.
waitN :: SSem -> Int -> STM ()
waitN (SSem s) i = do
v <- readTVar s
if v >= i
then writeTVar s $! v-i
else retry
-- | Signal that single unit of the semaphore is available. This increases the available quantity
-- by one.
signal :: SSem -> STM ()
signal = flip signalN 1
-- | Signal that many units of the semaphore are available. This changes the available quantity by
-- adding the passed size.
signalN :: SSem -> Int -> STM ()
signalN (SSem s) i = do
v <- readTVar s
writeTVar s $! v+i
-- | Non-retrying version of 'wait'. `tryWait s` is defined as `tryN s 1`
tryWait :: SSem -> STM (Maybe Int)
tryWait = flip tryWaitN 1
-- | Non-retrying version of waitN. It either takes the quantity from the semaphore like
-- waitN and returns `Just value taken` or finds insufficient quantity to take and returns
-- Nothing
tryWaitN :: SSem -> Int -> STM (Maybe Int)
tryWaitN (SSem s) i = do
v <- readTVar s
if v >= i
then do writeTVar s $! v-i
return (Just i)
else return Nothing
-- | Return the current quantity in the semaphore. This is potentially useful in a larger STM
-- transaciton and less useful as `atomically getValueSem :: IO Int` due to race conditions.
getValue :: SSem -> STM Int
getValue (SSem s) = readTVar s