concurrency-1.1.2.0: Control/Concurrent/Classy/Chan.hs
-- |
-- Module : Control.Concurrent.Classy.Chan
-- Copyright : (c) 2016 Michael Walker
-- License : MIT
-- Maintainer : Michael Walker <mike@barrucadu.co.uk>
-- Stability : stable
-- Portability : portable
--
-- Unbounded channels.
--
-- __Deviations:__ @Chan@ as defined here does not have an @Eq@
-- instance, this is because the @MonadConc@ @MVar@ type does not have
-- an @Eq@ constraint. The deprecated @unGetChan@ and @isEmptyCHan@
-- functions are not provided. Furthermore, the @getChanContents@
-- function is not provided as it needs unsafe I/O.
module Control.Concurrent.Classy.Chan
( -- * The 'Chan' type
Chan
-- * Operations
, newChan
, writeChan
, readChan
, dupChan
-- * Stream interface
, writeList2Chan
) where
import Control.Concurrent.Classy.MVar
import Control.Monad.Catch (mask_)
import Control.Monad.Conc.Class (MonadConc)
-- | 'Chan' is an abstract type representing an unbounded FIFO
-- channel.
--
-- @since 1.0.0.0
data Chan m a
= Chan (MVar m (Stream m a))
(MVar m (Stream m a)) -- Invariant: the Stream a is always an empty MVar
type Stream m a = MVar m (ChItem m a)
data ChItem m a = ChItem a (Stream m a)
-- | Build and returns a new instance of 'Chan'.
--
-- @since 1.0.0.0
newChan :: MonadConc m => m (Chan m a)
newChan = do
hole <- newEmptyMVar
readVar <- newMVar hole
writeVar <- newMVar hole
pure (Chan readVar writeVar)
-- | Write a value to a 'Chan'.
--
-- @since 1.0.0.0
writeChan :: MonadConc m => Chan m a -> a -> m ()
writeChan (Chan _ writeVar) val = do
new_hole <- newEmptyMVar
mask_ $ do
old_hole <- takeMVar writeVar
putMVar old_hole (ChItem val new_hole)
putMVar writeVar new_hole
-- | Read the next value from the 'Chan'.
--
-- @since 1.0.0.0
readChan :: MonadConc m => Chan m a -> m a
readChan (Chan readVar _) = modifyMVarMasked readVar $ \read_end -> do
(ChItem val new_read_end) <- readMVar read_end
pure (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.
--
-- @since 1.0.0.0
dupChan :: MonadConc m => Chan m a -> m (Chan m a)
dupChan (Chan _ writeVar) = do
hole <- readMVar writeVar
newReadVar <- newMVar hole
pure (Chan newReadVar writeVar)
-- | Write an entire list of items to a 'Chan'.
--
-- @since 1.0.0.0
writeList2Chan :: MonadConc m => Chan m a -> [a] -> m ()
writeList2Chan = mapM_ . writeChan