diff --git a/src/EuphApi/CloseableChan.hs b/src/EuphApi/CloseableChan.hs index fec6cea..219ec71 100644 --- a/src/EuphApi/CloseableChan.hs +++ b/src/EuphApi/CloseableChan.hs @@ -1,24 +1,39 @@ {-# LANGUAGE RecordWildCards #-} +-- | Chans that can be closed and reopened. +-- +-- While a 'CloseableChan' is closed (or locked), it can not be written to or read from. +-- Calls to 'writeChan' and 'readChan' are non-blocking while a chan is closed. +-- +-- If a thread is attempting to read from a chan using 'readChan' and that chan is closed, +-- the call to 'readChan' resumes and @Nothing@ is returned. + module EuphApi.CloseableChan ( CloseableChan - -- * IO versions - , newCloseableChan + -- * IO function versions + , newOpenChan + , newClosedChan , writeChan , readChan , closeChan + , openChan , emptyChan - -- * STM versions - , newCloseableChanSTM + -- * STM function versions + , newOpenChanSTM + , newClosedChanSTM , writeChanSTM , readChanSTM , closeChanSTM + , openChanSTM , emptyChanSTM ) where import Control.Concurrent.STM -import Control.Monad +-- | A 'Chan' that can be closed and opened again. +-- +-- Attempts to write to or read from a 'CloseableChan' while it is closed result +-- in a @Nothing@. data CloseableChan a = CloseableChan { cClosed :: TVar Bool , cChan :: TChan (Content a) @@ -31,12 +46,21 @@ data Content a = Value a - Functions as STM actions -} -newCloseableChanSTM :: STM (CloseableChan a) -newCloseableChanSTM = do +-- | See 'newOpenChan'. +newOpenChanSTM :: STM (CloseableChan a) +newOpenChanSTM = do cClosed <- newTVar False cChan <- newTChan return $ CloseableChan{..} +-- | See 'newClosedChan'. +newClosedChanSTM :: STM (CloseableChan a) +newClosedChanSTM = do + cClosed <- newTVar True + cChan <- newTChan + return $ CloseableChan{..} + +-- | See 'writeChan'. writeChanSTM :: CloseableChan a -> a -> STM (Maybe ()) writeChanSTM CloseableChan{..} a = do closed <- readTVar cClosed @@ -44,6 +68,7 @@ writeChanSTM CloseableChan{..} a = do then return Nothing else Just <$> writeTChan cChan (Value a) +-- | See 'readChan'. readChanSTM :: CloseableChan a -> STM (Maybe a) readChanSTM CloseableChan{..} = do closed <- readTVar cClosed @@ -57,11 +82,18 @@ readChanSTM CloseableChan{..} = do End -> readValue -- ignore End while reading normally Value v -> return v +-- | See 'closeChan'. closeChanSTM :: CloseableChan a -> STM () closeChanSTM CloseableChan{..} = do writeTVar cClosed True --writeTChan cChan End +-- | See 'openChan'. +openChanSTM :: CloseableChan a -> STM () +openChanSTM CloseableChan{..} = do + writeTVar cClosed False + +-- | See 'emptyChan'. emptyChanSTM :: CloseableChan a -> STM [a] emptyChanSTM CloseableChan{..} = do writeTChan cChan End @@ -77,17 +109,43 @@ emptyChanSTM CloseableChan{..} = do - Functions as IO actions -} -newCloseableChan :: IO (CloseableChan a) -newCloseableChan = atomically newCloseableChanSTM +-- | Create a new open 'CloseableChan'. +newOpenChan :: IO (CloseableChan a) +newOpenChan = atomically newOpenChanSTM +-- | Create a new closed 'CloseableChan'. +newClosedChan :: IO (CloseableChan a) +newClosedChan = atomically newClosedChanSTM + +-- | Attempt to write a value into the 'CloseableChan'. +-- +-- If the chan is open, succeeds with a @Just ()@. +-- If the chan is closed, fails with a @Nothing@. writeChan :: CloseableChan a -> a -> IO (Maybe ()) -writeChan chan = atomically . writeChanSTM chan +writeChan chan a = atomically $ writeChanSTM chan a +-- | Attempt to read a value @v@ from the 'CloseableChan'. +-- +-- If the chan is open, succeeds with a @Just v@. +-- If the chan is closed, fails with a @Nothing@. readChan :: CloseableChan a -> IO (Maybe a) readChan = atomically . readChanSTM +-- | Close a 'CloseableChan'. +-- Does nothing if chan is already closed. +-- +-- Performing this action un-blocks all calls to 'readChan'. closeChan :: CloseableChan a -> IO () closeChan = atomically . closeChanSTM +-- | Open a 'CloseableChan'. +-- Does nothing if chan is already open. +openChan :: CloseableChan a -> IO () +openChan = atomically . openChanSTM + +-- | Remove all items currently in the 'CloseableChan' and returns them in a list. +-- +-- This function also works while the chan is closed. +-- It is meant as a way to clean up the remaining values in a chan after it was closed. emptyChan :: CloseableChan a -> IO [a] emptyChan = atomically . emptyChanSTM