[project @ 2002-05-10 13:17:27 by simonmar]
[ghc-base.git] / Control / Concurrent / QSem.hs
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module      :  Control.Concurrent.QSem
4 -- Copyright   :  (c) The University of Glasgow 2001
5 -- License     :  BSD-style (see the file libraries/base/LICENSE)
6 -- 
7 -- Maintainer  :  libraries@haskell.org
8 -- Stability   :  experimental
9 -- Portability :  non-portable (concurrency)
10 --
11 -- Simple quantity semaphores.
12 --
13 -----------------------------------------------------------------------------
14
15 module Control.Concurrent.QSem
16         ( -- * Simple Quantity Semaphores
17           QSem,         -- abstract
18           newQSem,      -- :: Int  -> IO QSem
19           waitQSem,     -- :: QSem -> IO ()
20           signalQSem    -- :: QSem -> IO ()
21         ) where
22
23 import Prelude
24 import Control.Concurrent.MVar
25
26 -- General semaphores are also implemented readily in terms of shared
27 -- @MVar@s, only have to catch the case when the semaphore is tried
28 -- waited on when it is empty (==0). Implement this in the same way as
29 -- shared variables are implemented - maintaining a list of @MVar@s
30 -- representing threads currently waiting. The counter is a shared
31 -- variable, ensuring the mutual exclusion on its access.
32
33 -- |A 'QSem' is a simple quantity semaphore, in which the available
34 -- \"quantity\" is always dealt with in units of one.
35 newtype QSem = QSem (MVar (Int, [MVar ()]))
36
37 -- |Build a new 'QSem'
38 newQSem :: Int -> IO QSem
39 newQSem init = do
40    sem <- newMVar (init,[])
41    return (QSem sem)
42
43 -- |Wait for a unit to become available
44 waitQSem :: QSem -> IO ()
45 waitQSem (QSem sem) = do
46    (avail,blocked) <- takeMVar sem  -- gain ex. access
47    if avail > 0 then
48      putMVar sem (avail-1,[])
49     else do
50      block <- newEmptyMVar
51       {-
52         Stuff the reader at the back of the queue,
53         so as to preserve waiting order. A signalling
54         process then only have to pick the MVar at the
55         front of the blocked list.
56
57         The version of waitQSem given in the paper could
58         lead to starvation.
59       -}
60      putMVar sem (0, blocked++[block])
61      takeMVar block
62
63 -- |Signal that a unit of the 'QSem' is available
64 signalQSem :: QSem -> IO ()
65 signalQSem (QSem sem) = do
66    (avail,blocked) <- takeMVar sem
67    case blocked of
68      [] -> putMVar sem (avail+1,[])
69
70      (block:blocked') -> do
71            putMVar sem (0,blocked')
72            putMVar block ()