From c8dbebaa9851a2f96c202c176998fb4af49e87bf Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 8 Feb 2020 14:59:54 +0000 Subject: [PATCH] Add broadcaster module --- src/Forest/Broadcast.hs | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/Forest/Broadcast.hs diff --git a/src/Forest/Broadcast.hs b/src/Forest/Broadcast.hs new file mode 100644 index 0000000..2c319c6 --- /dev/null +++ b/src/Forest/Broadcast.hs @@ -0,0 +1,52 @@ +-- | A 'Broadcaster' allows threads to 'broadcast' values to 'Listeners' +-- attached to that broadcaster. A value that is sent through a broadcaster will +-- arrive exactly once at each attached listener and can then be collected by +-- calling 'listen'. +-- +-- All functions included in this module should be threadsafe. Be sure to read +-- the warning on the 'broadcast' function. + +module Forest.Broadcast + ( Broadcaster + , Listener + , newBroadcaster + , attachListener + , broadcast + , listen + ) where + +import Control.Concurrent.Chan + +-- | A 'Broadcaster' can broadcast values to all attached 'Listener's +newtype Broadcaster a = Broadcaster (Chan a) + +-- | A 'Listener' receives values from the 'Broadcaster' it is attached to +newtype Listener a = Listener (Chan a) + +-- | Create a new 'Broadcaster' +newBroadcaster :: IO (Broadcaster a) +newBroadcaster = Broadcaster <$> newChan + +-- | Create a new 'Listener' that is attached to a 'Broadcaster' +attachListener :: Broadcaster a -> IO (Listener a) +attachListener (Broadcaster chan) = Listener <$> dupChan chan + +-- | Send a value through the 'Broadcaster'. That value will arrive exactly once +-- at all 'Listener's attached to this broadcaster via 'attachListener'. +-- +-- Warning: During this function call, no exception should occur or elements may +-- build up in the broadcaster, leading to a memory/space leak. +broadcast :: Broadcaster a -> a -> IO () +-- Because the same function that puts something into the broadcaster channel +-- also immediately reads something from that channel, there is no build-up of +-- values in the broadcaster channel, as one element is removed for each element +-- written. Since the broadcaster channel is separate from the listener +-- channels, no event is swallowed accidentally. +-- +-- If some exception happens after the write operation succeeds but before the +-- read operation finishes, elements can build up in the broadcast channel. +broadcast (Broadcaster chan) value = writeChan chan value <* readChan chan + +-- | Read the next value from the 'Listener'. Blocks when the listener is empty. +listen :: Listener a -> IO a +listen (Listener chan) = readChan chan