Movatterモバイル変換
[0]ホーム
{-# LANGUAGE Trustworthy #-}{-# LANGUAGE CPP #-}------------------------------------------------------------------------------- |-- 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.---- The channels are implemented with @MVar@s and therefore inherit all the-- caveats that apply to @MVar@s (possibility of races, deadlocks etc). The-- stm (software transactional memory) library has a more robust implementation-- of channels called @TChan@s.-------------------------------------------------------------------------------moduleControl.Concurrent.Chan(-- * The 'Chan' typeChan,-- abstract-- * OperationsnewChan,writeChan,readChan,dupChan,-- * Stream interfacegetChanContents,writeList2Chan,)whereimportSystem.IO.Unsafe(unsafeInterleaveIO)importControl.Concurrent.MVarimportControl.Exception(mask_)#define _UPK_(x) {-# UNPACK #-} !(x)-- 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_UPK_(MVar(Streama))_UPK_(MVar(Streama))-- Invariant: the Stream a is always an empty MVarderivingEq-- ^ @since 4.4.0.0typeStreama=MVar(ChItema)dataChItema=ChItema_UPK_(Streama)-- benchmarks show that unboxing the MVar here is worthwhile, because-- although it leads to higher allocation, the channel data takes up-- less space and is therefore quicker to GC.-- 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<-newEmptyMVarmask_$doold_hole<-takeMVarwriteVarputMVarold_hole(ChItemvalnew_hole)putMVarwriteVarnew_hole-- The reason we don't simply do this:---- modifyMVar_ writeVar $ \old_hole -> do-- putMVar old_hole (ChItem val new_hole)-- return new_hole---- is because if an asynchronous exception is received after the 'putMVar'-- completes and before modifyMVar_ installs the new value, it will set the-- Chan's write end to a filled hole.-- |Read the next value from the 'Chan'. Blocks when the channel is empty. Since-- the read end of a channel is an 'MVar', this operation inherits fairness-- guarantees of 'MVar's (e.g. threads blocked in this operation are woken up in-- FIFO order).---- Throws 'BlockedIndefinitelyOnMVar' when the channel is empty and no other-- thread holds a reference to the channel.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.---- (Note that a duplicated channel is not equal to its original.-- So: @fmap (c /=) $ dupChan c@ returns @True@ for all @c@.)dupChan::Chana->IO(Chana)dupChan(Chan_writeVar)=dohole<-readMVarwriteVarnewReadVar<-newMVarholereturn(ChannewReadVarwriteVar)-- 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)
[8]ページ先頭