{-# OPTIONS_GHC -Wall -fwarn-tabs #-} {-# LANGUAGE CPP, DeriveDataTypeable #-} #if __GLASGOW_HASKELL__ >= 701 {-# LANGUAGE Safe #-} #endif ---------------------------------------------------------------- -- 2021.10.17 -- | -- Module : Control.Concurrent.STM.TMQueue -- Copyright : Copyright (c) 2011--2021 wren gayle romano -- License : BSD -- Maintainer : wren@cpan.org -- Stability : provisional -- Portability : non-portable (GHC STM, DeriveDataTypeable) -- -- A version of "Control.Concurrent.STM.TQueue" where the queue is -- closeable. This is similar to a @TQueue (Maybe a)@ with a -- monotonicity guarantee that once there's a @Nothing@ there will -- always be @Nothing@. -- -- /Since: 2.0.0/ ---------------------------------------------------------------- module Control.Concurrent.STM.TMQueue ( -- * The TMQueue type TMQueue() -- ** Creating TMQueues , newTMQueue , newTMQueueIO -- ** Reading from TMQueues , readTMQueue , tryReadTMQueue , peekTMQueue , tryPeekTMQueue -- ** Writing to TMQueues , writeTMQueue , unGetTMQueue -- ** Closing TMQueues , closeTMQueue -- ** Predicates , isClosedTMQueue , isEmptyTMQueue ) where import Data.Typeable (Typeable) #if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>)) #endif import Control.Monad.STM (STM) import Control.Concurrent.STM.TVar import Control.Concurrent.STM.TQueue -- N.B., GHC only ---------------------------------------------------------------- -- | @TMQueue@ is an abstract type representing a closeable FIFO -- queue. data TMQueue a = TMQueue {-# UNPACK #-} !(TVar Bool) {-# UNPACK #-} !(TQueue a) deriving Typeable -- | Build and returns a new instance of @TMQueue@. newTMQueue :: STM (TMQueue a) newTMQueue = do closed <- newTVar False queue <- newTQueue return (TMQueue closed queue) -- | @IO@ version of 'newTMQueue'. This is useful for creating -- top-level @TMQueue@s using 'System.IO.Unsafe.unsafePerformIO', -- because using 'Control.Monad.STM.atomically' inside -- 'System.IO.Unsafe.unsafePerformIO' isn't possible. newTMQueueIO :: IO (TMQueue a) newTMQueueIO = do closed <- newTVarIO False queue <- newTQueueIO return (TMQueue closed queue) -- | Read the next value from the @TMQueue@, retrying if the queue -- is empty (and not closed). We return @Nothing@ immediately if -- the queue is closed and empty. readTMQueue :: TMQueue a -> STM (Maybe a) readTMQueue (TMQueue closed queue) = do b <- readTVar closed if b then tryReadTQueue queue else Just <$> readTQueue queue {- -- The above is lazier reading from @queue@, and slightly optimized, compared to the clearer: readTMQueue (TMQueue closed queue) = do b <- isEmptyTQueue queue b' <- readTVar closed if b && b' then return Nothing else Just <$> readTQueue queue -- TODO: compare Core and benchmarks; is the loss of clarity worth it? -} -- | A version of 'readTMQueue' which does not retry. Instead it -- returns @Just Nothing@ if the queue is open but no value is -- available; it still returns @Nothing@ if the queue is closed -- and empty. tryReadTMQueue :: TMQueue a -> STM (Maybe (Maybe a)) tryReadTMQueue (TMQueue closed queue) = do b <- readTVar closed if b then fmap Just <$> tryReadTQueue queue else Just <$> tryReadTQueue queue {- -- The above is lazier reading from @queue@ (and removes an extraneous isEmptyTQueue when using the compatibility layer) than the clearer: tryReadTMQueue (TMQueue closed queue) = do b <- isEmptyTQueue queue b' <- readTVar closed if b && b' then return Nothing else Just <$> tryReadTQueue queue -- TODO: compare Core and benchmarks; is the loss of clarity worth it? -} -- | Get the next value from the @TMQueue@ without removing it, -- retrying if the queue is empty. peekTMQueue :: TMQueue a -> STM (Maybe a) peekTMQueue (TMQueue closed queue) = do b <- readTVar closed if b then do b' <- isEmptyTQueue queue if b' then return Nothing else Just <$> peekTQueue queue else Just <$> peekTQueue queue {- -- The above is lazier reading from @queue@ than the clearer: peekTMQueue (TMQueue closed queue) = do b <- isEmptyTQueue queue b' <- readTVar closed if b && b' then return Nothing else Just <$> peekTQueue queue -- TODO: compare Core and benchmarks; is the loss of clarity worth it? -} -- | A version of 'peekTMQueue' which does not retry. Instead it -- returns @Just Nothing@ if the queue is open but no value is -- available; it still returns @Nothing@ if the queue is closed -- and empty. tryPeekTMQueue :: TMQueue a -> STM (Maybe (Maybe a)) tryPeekTMQueue (TMQueue closed queue) = do b <- readTVar closed if b then fmap Just <$> tryPeekTQueue queue else Just <$> tryPeekTQueue queue {- -- The above is lazier reading from @queue@ (and removes an extraneous isEmptyTQueue when using the compatibility layer) than the clearer: tryPeekTMQueue (TMQueue closed queue) = do b <- isEmptyTQueue queue b' <- readTVar closed if b && b' then return Nothing else Just <$> tryPeekTQueue queue -- TODO: compare Core and benchmarks; is the loss of clarity worth it? -} -- | Write a value to a @TMQueue@. If the queue is closed then the -- value is silently discarded. Use 'isClosedTMQueue' to determine -- if the queue is closed before writing, as needed. writeTMQueue :: TMQueue a -> a -> STM () writeTMQueue (TMQueue closed queue) x = do b <- readTVar closed if b then return () -- discard silently else writeTQueue queue x -- | Put a data item back onto a queue, where it will be the next -- item read. If the queue is closed then the value is silently -- discarded; you can use 'peekTMQueue' to circumvent this in certain -- circumstances. unGetTMQueue :: TMQueue a -> a -> STM () unGetTMQueue (TMQueue closed queue) x = do b <- readTVar closed if b then return () -- discard silently else unGetTQueue queue x -- | Closes the @TMQueue@, preventing any further writes. closeTMQueue :: TMQueue a -> STM () closeTMQueue (TMQueue closed _queue) = writeTVar closed True -- | Returns @True@ if the supplied @TMQueue@ has been closed. isClosedTMQueue :: TMQueue a -> STM Bool isClosedTMQueue (TMQueue closed _queue) = readTVar closed {- -- | Returns @True@ if the supplied @TMQueue@ has been closed. isClosedTMQueueIO :: TMQueue a -> IO Bool isClosedTMQueueIO (TMQueue closed _queue) = readTVarIO closed -} -- | Returns @True@ if the supplied @TMQueue@ is empty. isEmptyTMQueue :: TMQueue a -> STM Bool isEmptyTMQueue (TMQueue _closed queue) = isEmptyTQueue queue ---------------------------------------------------------------- ----------------------------------------------------------- fin.