{-# LANGUAGE BangPatterns #-}------------------------------------------------------------------------------- |-- Module : Control.Concurrent.Chan.Strict-- Copyright : (c) The University of Glasgow 2001, Don Stewart 2007-- License : BSD-style-- -- Maintainer : dons@galois.com-- Stability : experimental-- Portability : non-portable (concurrency)---- Unbounded, element-strict channels. Elements will be evaluated to-- WHNF on entering the channel. For some concurrency applications, this-- is more desirable than passing an unevaluted thunk through the channel-- (for instance, it guarantees the node willl be evaluated to WHNF in a-- worker thead).---- Element-strict channes may potentially use more memory than lazy-- channels-------------------------------------------------------------------------------moduleControl.Concurrent.Chan.Strict(-- * 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.MVar.StrictimportControl.Parallel.Strategies-- 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))typeStreama=MVar(ChItema)dataChItema=ChItem!a(Streama)instanceNFData(MVara)instanceNFDataa=>NFData(ChItema)wherernf(ChItemas)=rnfa`seq`rnfs-- @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::NFDataa=>IO(Chana)newChan=dohole<-newEmptyMVarreadm<-newMVarholewrite<-newMVarholereturn(Chanreadmwrite)-- 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::NFDataa=>Chana->a->IO()writeChan(Chan_readwrite)val=donew_hole<-newEmptyMVarmodifyMVar_write$\old_hole->doputMVarold_hole$!ChItemvalnew_holereturnnew_hole-- |Read the next value from the 'Chan'.readChan::NFDataa=>Chana->IOareadChan(Chanreadm_write)=domodifyMVarreadm$\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::NFDataa=>Chana->IO(Chana)dupChan(Chan_readwrite)=dohole<-readMVarwritenew_read<-newMVarholereturn(Channew_readwrite)-- |Put a data item back onto a channel, where it will be the next item read.unGetChan::NFDataa=>Chana->a->IO()unGetChan(Chanreadm_write)val=donew_read_end<-newEmptyMVarmodifyMVar_readm$\read_end->doputMVarnew_read_end(ChItemvalread_end)returnnew_read_end-- |Returns 'True' if the supplied 'Chan' is empty.isEmptyChan::NFDataa=>Chana->IOBoolisEmptyChan(Chanreadmwrite)=dowithMVarreadm$\r->dow<-readMVarwriteleteq=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::NFDataa=>Chana->IO[a]getChanContentsch=unsafeInterleaveIO$dox<-readChanchxs<-getChanContentschreturn(x:xs)-- |Write an entire list of items to a 'Chan'.writeList2Chan::NFDataa=>Chana->[a]->IO()writeList2Chan=mapM_.writeChan