Fix bugs regarding asynchronous exceptions and laziness in Control.Concurrent.SampleVar
[ghc-base.git] / Control / Concurrent / SampleVar.hs
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module      :  Control.Concurrent.SampleVar
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 -- Sample variables
12 --
13 -----------------------------------------------------------------------------
14
15 module Control.Concurrent.SampleVar
16        (
17          -- * Sample Variables
18          SampleVar,         -- :: type _ =
19  
20          newEmptySampleVar, -- :: IO (SampleVar a)
21          newSampleVar,      -- :: a -> IO (SampleVar a)
22          emptySampleVar,    -- :: SampleVar a -> IO ()
23          readSampleVar,     -- :: SampleVar a -> IO a
24          writeSampleVar,    -- :: SampleVar a -> a -> IO ()
25          isEmptySampleVar,  -- :: SampleVar a -> IO Bool
26
27        ) where
28
29 import Prelude
30
31 import Control.Concurrent.MVar
32
33 import Control.Exception ( block )
34
35 -- |
36 -- Sample variables are slightly different from a normal 'MVar':
37 -- 
38 --  * Reading an empty 'SampleVar' causes the reader to block.
39 --    (same as 'takeMVar' on empty 'MVar')
40 -- 
41 --  * Reading a filled 'SampleVar' empties it and returns value.
42 --    (same as 'takeMVar')
43 -- 
44 --  * Writing to an empty 'SampleVar' fills it with a value, and
45 --    potentially, wakes up a blocked reader (same as for 'putMVar' on
46 --    empty 'MVar').
47 --
48 --  * Writing to a filled 'SampleVar' overwrites the current value.
49 --    (different from 'putMVar' on full 'MVar'.)
50
51 type SampleVar a
52  = MVar (Int,           -- 1  == full
53                         -- 0  == empty
54                         -- <0 no of readers blocked
55           MVar a)
56
57 -- |Build a new, empty, 'SampleVar'
58 newEmptySampleVar :: IO (SampleVar a)
59 newEmptySampleVar = do
60    v <- newEmptyMVar
61    newMVar (0,v)
62
63 -- |Build a 'SampleVar' with an initial value.
64 newSampleVar :: a -> IO (SampleVar a)
65 newSampleVar a = do
66    v <- newMVar a
67    newMVar (1,v)
68
69 -- |If the SampleVar is full, leave it empty.  Otherwise, do nothing.
70 emptySampleVar :: SampleVar a -> IO ()
71 emptySampleVar v = block $ do
72    s@(readers, var) <- block $ takeMVar v
73    if readers > 0 then do
74      _ <- takeMVar var
75      putMVar v (0,var)
76     else
77      putMVar v s
78
79 -- |Wait for a value to become available, then take it and return.
80 readSampleVar :: SampleVar a -> IO a
81 readSampleVar svar = block $ do
82 --
83 -- filled => make empty and grab sample
84 -- not filled => try to grab value, empty when read val.
85 --
86    (readers,val) <- takeMVar svar
87    let readers' = readers-1
88    readers' `seq` putMVar svar (readers',val)
89    takeMVar val
90
91 -- |Write a value into the 'SampleVar', overwriting any previous value that
92 -- was there.
93 writeSampleVar :: SampleVar a -> a -> IO ()
94 writeSampleVar svar v = block $ do
95 --
96 -- filled => overwrite
97 -- not filled => fill, write val
98 --
99    s@(readers,val) <- takeMVar svar
100    case readers of
101      1 ->
102        swapMVar val v >>
103        putMVar svar s
104      _ ->
105        putMVar val v >>
106        let readers' = min 1 (readers+1)
107        in readers' `seq` putMVar svar (readers', val)
108
109 -- | Returns 'True' if the 'SampleVar' is currently empty.
110 --
111 -- Note that this function is only useful if you know that no other
112 -- threads can be modifying the state of the 'SampleVar', because
113 -- otherwise the state of the 'SampleVar' may have changed by the time
114 -- you see the result of 'isEmptySampleVar'.
115 --
116 isEmptySampleVar :: SampleVar a -> IO Bool
117 isEmptySampleVar svar = do
118    (readers, _) <- readMVar svar
119    return (readers == 0)
120