summaryrefslogtreecommitdiff
path: root/libraries/base/Control/Concurrent/Chan.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/base/Control/Concurrent/Chan.hs')
-rw-r--r--libraries/base/Control/Concurrent/Chan.hs119
1 files changed, 119 insertions, 0 deletions
diff --git a/libraries/base/Control/Concurrent/Chan.hs b/libraries/base/Control/Concurrent/Chan.hs
new file mode 100644
index 0000000000..29423e1d84
--- /dev/null
+++ b/libraries/base/Control/Concurrent/Chan.hs
@@ -0,0 +1,119 @@
+-----------------------------------------------------------------------------
+--
+-- Module : Control.Concurrent.Chan
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file libraries/core/LICENSE)
+--
+-- Maintainer : libraries@haskell.org
+-- Stability : experimental
+-- Portability : non-portable
+--
+-- $Id: Chan.hs,v 1.1 2001/06/28 14:15:02 simonmar Exp $
+--
+-- Standard, unbounded channel abstraction.
+--
+-----------------------------------------------------------------------------
+
+module Control.Concurrent.Chan
+ ( Chan -- abstract
+
+ -- creator
+ , newChan -- :: IO (Chan a)
+
+ -- operators
+ , writeChan -- :: Chan a -> a -> IO ()
+ , readChan -- :: Chan a -> IO a
+ , dupChan -- :: Chan a -> IO (Chan a)
+ , unGetChan -- :: Chan a -> a -> IO ()
+
+ , isEmptyChan -- :: Chan a -> IO Bool
+
+ -- stream interface
+ , getChanContents -- :: Chan a -> IO [a]
+ , writeList2Chan -- :: Chan a -> [a] -> IO ()
+
+ ) where
+
+import Prelude
+
+import System.IO.Unsafe ( unsafeInterleaveIO )
+import Control.Concurrent.MVar
+
+-- A channel is represented by two @MVar@s keeping track of the two ends
+-- of the channel contents,i.e., the read- and write ends. Empty @MVar@s
+-- are used to handle consumers trying to read from an empty channel.
+
+data Chan a
+ = Chan (MVar (Stream a))
+ (MVar (Stream a))
+
+type Stream a = MVar (ChItem a)
+
+data ChItem a = ChItem a (Stream a)
+
+-- See the Concurrent Haskell paper for a diagram explaining the
+-- how the different channel operations proceed.
+
+-- @newChan@ sets up the read and write end of a channel by initialising
+-- these two @MVar@s with an empty @MVar@.
+
+newChan :: IO (Chan a)
+newChan = do
+ hole <- newEmptyMVar
+ read <- newMVar hole
+ write <- newMVar hole
+ return (Chan read write)
+
+-- To put an element on a channel, a new hole at the write end is created.
+-- What was previously the empty @MVar@ at the back of the channel is then
+-- filled in with a new stream element holding the entered value and the
+-- new hole.
+
+writeChan :: Chan a -> a -> IO ()
+writeChan (Chan _read write) val = do
+ new_hole <- newEmptyMVar
+ modifyMVar_ write $ \old_hole -> do
+ putMVar old_hole (ChItem val new_hole)
+ return new_hole
+
+readChan :: Chan a -> IO a
+readChan (Chan read _write) = do
+ modifyMVar read $ \read_end -> do
+ (ChItem val new_read_end) <- readMVar read_end
+ -- Use readMVar here, not takeMVar,
+ -- else dupChan doesn't work
+ return (new_read_end, val)
+
+dupChan :: Chan a -> IO (Chan a)
+dupChan (Chan _read write) = do
+ hole <- readMVar write
+ new_read <- newMVar hole
+ return (Chan new_read write)
+
+unGetChan :: Chan a -> a -> IO ()
+unGetChan (Chan read _write) val = do
+ new_read_end <- newEmptyMVar
+ modifyMVar_ read $ \read_end -> do
+ putMVar new_read_end (ChItem val read_end)
+ return new_read_end
+
+isEmptyChan :: Chan a -> IO Bool
+isEmptyChan (Chan read write) = do
+ withMVar read $ \r -> do
+ w <- readMVar write
+ let eq = r == w
+ eq `seq` return eq
+
+-- Operators for interfacing with functional streams.
+
+getChanContents :: Chan a -> IO [a]
+getChanContents ch
+ = unsafeInterleaveIO (do
+ x <- readChan ch
+ xs <- getChanContents ch
+ return (x:xs)
+ )
+
+-------------
+writeList2Chan :: Chan a -> [a] -> IO ()
+writeList2Chan ch ls = sequence_ (map (writeChan ch) ls)