broadcast-chan 0.1.0 → 0.1.1
raw patch · 2 files changed
+23/−15 lines, 2 files
Files
- Control/Concurrent/BroadcastChan.hs +16/−11
- broadcast-chan.cabal +7/−4
Control/Concurrent/BroadcastChan.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Safe #-} ------------------------------------------------------------------------------- -- | -- Module : Contro.Concurrent.BroadcastChan@@ -50,11 +50,8 @@ type Out = 'Out -- | The abstract type representing the read or write end of a 'BroadcastChan'.-data BroadcastChan :: Direction -> * -> * where- WriteEnd :: {-# UNPACK #-} !(MVar (Stream a)) -> BroadcastChan In a- ReadEnd :: {-# UNPACK #-} !(MVar (Stream a)) -> BroadcastChan Out a--deriving instance Eq (BroadcastChan i a)+newtype BroadcastChan (d :: Direction) a = BChan (MVar (Stream a))+ deriving (Eq) type Stream a = MVar (ChItem a) @@ -65,13 +62,13 @@ newBroadcastChan = do hole <- newEmptyMVar writeVar <- newMVar hole- return (WriteEnd writeVar)+ return (BChan writeVar) -- | Write a value to write end of a 'BroadcastChan'. Any messages written -- while there are no live read ends can be immediately garbage collected, thus -- avoiding space leaks. writeBChan :: BroadcastChan In a -> a -> IO ()-writeBChan (WriteEnd writeVar) val = do+writeBChan (BChan writeVar) val = do new_hole <- newEmptyMVar mask_ $ do old_hole <- takeMVar writeVar@@ -80,17 +77,25 @@ -- | Read the next value from the read end of a 'BroadcastChan'. readBChan :: BroadcastChan Out a -> IO a-readBChan (ReadEnd readVar) = do+readBChan (BChan readVar) = do modifyMVarMasked readVar $ \read_end -> do -- Note [modifyMVarMasked] (ChItem val new_read_end) <- readMVar read_end -- Use readMVar here, not takeMVar, -- else dupBroadcastChan doesn't work return (new_read_end, val) +-- Note [modifyMVarMasked]+-- This prevents a theoretical deadlock if an asynchronous exception+-- happens during the readMVar while the MVar is empty. In that case+-- the read_end MVar will be left empty, and subsequent readers will+-- deadlock. Using modifyMVarMasked prevents this. The deadlock can+-- be reproduced, but only by expanding readMVar and inserting an+-- artificial yield between its takeMVar and putMVar operations.+ -- | Create a new read end for a 'BroadcastChan'. Will receive all messages -- written to the channel's write end after the read end's creation. newBChanListener :: BroadcastChan In a -> IO (BroadcastChan Out a)-newBChanListener (WriteEnd writeVar) = do+newBChanListener (BChan writeVar) = do hole <- readMVar writeVar newReadVar <- newMVar hole- return (ReadEnd newReadVar)+ return (BChan newReadVar)
broadcast-chan.cabal view
@@ -1,5 +1,5 @@ Name: broadcast-chan-Version: 0.1.0+Version: 0.1.1 Homepage: https://github.com/merijn/broadcast-chan Bug-Reports: https://github.com/merijn/broadcast-chan/issues@@ -36,15 +36,18 @@ Library Default-Language: Haskell2010 GHC-Options: -Wall -fno-warn-unused-do-bind- GHC-Prof-Options: -auto-all -caf-all -rtsopts Exposed-Modules: Control.Concurrent.BroadcastChan Build-Depends: base >= 4 && < 5 Source-Repository head+ Type: git+ Location: ssh://github.com:merijn/broadcast-chan.git++Source-Repository head Type: mercurial- Location: https://bitbucket.org/merijnv/broadcast-chan+ Location: git+ssh://github.com:merijn/broadcast-chan.git Source-Repository head Type: mercurial- Location: git+ssh://github.com:merijn/broadcast-chan+ Location: https://bitbucket.org/merijnv/broadcast-chan