% % (c) The GRASP/AQUA Project, Glasgow University, 1995 % \section[Semaphore]{Quantity semaphores} General/quantity semaphores \begin{code} module Semaphore ( {- abstract -} QSem, newQSem, -- :: Int -> IO QSem waitQSem, -- :: QSem -> IO () signalQSem, -- :: QSem -> IO () {- abstract -} QSemN, newQSemN, -- :: Int -> IO QSemN waitQSemN, -- :: QSemN -> Int -> IO () signalQSemN -- :: QSemN -> Int -> IO () ) where import PrelConc \end{code} General semaphores are also implemented readily in terms of shared @MVar@s, only have to catch the case when the semaphore is tried waited on when it is empty (==0). Implement this in the same way as shared variables are implemented - maintaining a list of @MVar@s representing threads currently waiting. The counter is a shared variable, ensuring the mutual exclusion on its access. \begin{code} newtype QSem = QSem (MVar (Int, [MVar ()])) newQSem :: Int -> IO QSem newQSem init = do sem <- newMVar (init,[]) return (QSem sem) waitQSem :: QSem -> IO () waitQSem (QSem sem) = do (avail,blocked) <- takeMVar sem -- gain ex. access if avail > 0 then putMVar sem (avail-1,[]) else do block <- newEmptyMVar {- Stuff the reader at the back of the queue, so as to preserve waiting order. A signalling process then only have to pick the MVar at the front of the blocked list. The version of waitQSem given in the paper could lead to starvation. -} putMVar sem (0, blocked++[block]) takeMVar block signalQSem :: QSem -> IO () signalQSem (QSem sem) = do (avail,blocked) <- takeMVar sem case blocked of [] -> putMVar sem (avail+1,[]) (block:blocked') -> do putMVar sem (0,blocked') putMVar block () \end{code} \begin{code} newtype QSemN = QSemN (MVar (Int,[(Int,MVar ())])) newQSemN :: Int -> IO QSemN newQSemN init = do sem <- newMVar (init,[]) return (QSemN sem) waitQSemN :: QSemN -> Int -> IO () waitQSemN (QSemN sem) sz = do (avail,blocked) <- takeMVar sem -- gain ex. access if (avail - sz) > 0 then -- discharging 'sz' still leaves the semaphore -- in an 'unblocked' state. putMVar sem (avail-sz,[]) else do block <- newEmptyMVar putMVar sem (avail, blocked++[(sz,block)]) takeMVar block signalQSemN :: QSemN -> Int -> IO () signalQSemN (QSemN sem) n = do (avail,blocked) <- takeMVar sem (avail',blocked') <- free (avail+n) blocked putMVar sem (avail',blocked') where free avail [] = return (avail,[]) free avail ((req,block):blocked) | avail >= req = do putMVar block () free (avail-req) blocked | otherwise = do (avail',blocked') <- free avail blocked return (avail',(req,block):blocked') \end{code}