------------------------------------------------------------------------------- |-- Module : Control.Concurrent.Chan-- Copyright : (c) The University of Glasgow 2001-- License : BSD-style (see the file libraries/base/LICENSE)-- -- Maintainer : libraries@haskell.org-- Stability : experimental-- Portability : non-portable (concurrency)---- Unbounded channels.-------------------------------------------------------------------------------moduleControl.Concurrent.Chan(-- * The 'Chan' typeChan,-- abstract-- * OperationsnewChan,-- :: IO (Chan a)writeChan,-- :: Chan a -> a -> IO ()readChan,-- :: Chan a -> IO adupChan,-- :: Chan a -> IO (Chan a)unGetChan,-- :: Chan a -> a -> IO ()isEmptyChan,-- :: Chan a -> IO Bool-- * Stream interfacegetChanContents,-- :: Chan a -> IO [a]writeList2Chan,-- :: Chan a -> [a] -> IO ())whereimportPreludeimportSystem.IO.Unsafe(unsafeInterleaveIO)importControl.Concurrent.MVarimportData.Typeable#include "Typeable.h"-- 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.-- |'Chan' is an abstract type representing an unbounded FIFO channel.dataChana=Chan(MVar(Streama))(MVar(Streama))INSTANCE_TYPEABLE1(Chan,chanTc,"Chan")typeStreama=MVar(ChItema)dataChItema=ChItema(Streama)-- 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@.-- |Build and returns a new instance of 'Chan'.newChan::IO(Chana)newChan=dohole<-newEmptyMVarreadVar<-newMVarholewriteVar<-newMVarholereturn(ChanreadVarwriteVar)-- 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.-- |Write a value to a 'Chan'.writeChan::Chana->a->IO()writeChan(Chan_writeVar)val=donew_hole<-newEmptyMVarmodifyMVar_writeVar$\old_hole->doputMVarold_hole(ChItemvalnew_hole)returnnew_hole-- |Read the next value from the 'Chan'.readChan::Chana->IOareadChan(ChanreadVar_)=domodifyMVarreadVar$\read_end->do(ChItemvalnew_read_end)<-readMVarread_end-- Use readMVar here, not takeMVar,-- else dupChan doesn't workreturn(new_read_end,val)-- |Duplicate a 'Chan': the duplicate channel begins empty, but data written to-- either channel from then on will be available from both. Hence this creates-- a kind of broadcast channel, where data written by anyone is seen by-- everyone else.dupChan::Chana->IO(Chana)dupChan(Chan_writeVar)=dohole<-readMVarwriteVarnewReadVar<-newMVarholereturn(ChannewReadVarwriteVar)-- |Put a data item back onto a channel, where it will be the next item read.unGetChan::Chana->a->IO()unGetChan(ChanreadVar_)val=donew_read_end<-newEmptyMVarmodifyMVar_readVar$\read_end->doputMVarnew_read_end(ChItemvalread_end)returnnew_read_end-- |Returns 'True' if the supplied 'Chan' is empty.isEmptyChan::Chana->IOBoolisEmptyChan(ChanreadVarwriteVar)=dowithMVarreadVar$\r->dow<-readMVarwriteVarleteq=r==weq`seq`returneq-- Operators for interfacing with functional streams.-- |Return a lazy list representing the contents of the supplied-- 'Chan', much like 'System.IO.hGetContents'.getChanContents::Chana->IO[a]getChanContentsch=unsafeInterleaveIO(dox<-readChanchxs<-getChanContentschreturn(x:xs))-- |Write an entire list of items to a 'Chan'.writeList2Chan::Chana->[a]->IO()writeList2Chanchls=sequence_(map(writeChanch)ls)