-- | -- Module : Control.Concurrent.Classy.STM.TQueue -- Copyright : (c) 2016 Michael Walker -- License : MIT -- Maintainer : Michael Walker <mike@barrucadu.co.uk> -- Stability : stable -- Portability : portable -- -- A 'TQueue' is like a 'TChan', with two important differences: -- -- * it has faster throughput than both 'TChan' and 'Chan' (although -- the costs are amortised, so the cost of individual operations -- can vary a lot). -- -- * it does /not/ provide equivalents of the 'dupTChan' and -- 'cloneTChan' operations. -- -- The implementation is based on the traditional purely-functional -- queue representation that uses two lists to obtain amortised /O(1)/ -- enqueue and dequeue operations. -- -- __Deviations:__ @TQueue@ as defined here does not have an @Eq@ -- instance, this is because the @MonadSTM@ @TVar@ type does not have -- an @Eq@ constraint. Furthermore, the @newTQueueIO@ function is not -- provided. module Control.Concurrent.Classy.STM.TQueue ( -- * TQueue TQueue , newTQueue , readTQueue , tryReadTQueue , flushTQueue , peekTQueue , tryPeekTQueue , writeTQueue , unGetTQueue , isEmptyTQueue ) where import Control.Monad (unless) import Control.Monad.STM.Class -- | 'TQueue' is an abstract type representing an unbounded FIFO channel. -- -- @since 1.0.0.0 data TQueue stm a = TQueue (TVar stm [a]) (TVar stm [a]) -- | Build and returns a new instance of 'TQueue' -- -- @since 1.0.0.0 newTQueue :: MonadSTM stm => stm (TQueue stm a) newTQueue = do readT <- newTVar [] writeT <- newTVar [] pure (TQueue readT writeT) -- | Write a value to a 'TQueue'. -- -- @since 1.0.0.0 writeTQueue :: MonadSTM stm => TQueue stm a -> a -> stm () writeTQueue (TQueue _ writeT) a = do listend <- readTVar writeT writeTVar writeT (a:listend) -- | Read the next value from the 'TQueue'. -- -- @since 1.0.0.0 readTQueue :: MonadSTM stm => TQueue stm a -> stm a readTQueue (TQueue readT writeT) = do xs <- readTVar readT case xs of (x:xs') -> do writeTVar readT xs' pure x [] -> do ys <- readTVar writeT case ys of [] -> retry _ -> do let (z:zs) = reverse ys -- NB. lazy: we want the transaction to be -- short, otherwise it will conflict writeTVar writeT [] writeTVar readT zs pure z -- | A version of 'readTQueue' which does not retry. Instead it -- returns @Nothing@ if no value is available. -- -- @since 1.0.0.0 tryReadTQueue :: MonadSTM stm => TQueue stm a -> stm (Maybe a) tryReadTQueue c = (Just <$> readTQueue c) `orElse` pure Nothing -- | Efficiently read the entire contents of a 'TQueue' into a list. This -- function never retries. -- -- @since 1.6.1.0 flushTQueue :: MonadSTM stm => TQueue stm a -> stm [a] flushTQueue (TQueue r w) = do xs <- readTVar r ys <- readTVar w unless (null xs) $ writeTVar r [] unless (null ys) $ writeTVar w [] pure (xs ++ reverse ys) -- | Get the next value from the @TQueue@ without removing it, -- retrying if the channel is empty. -- -- @since 1.0.0.0 peekTQueue :: MonadSTM stm => TQueue stm a -> stm a peekTQueue (TQueue readT writeT) = do xs <- readTVar readT case xs of (x:_) -> pure x [] -> do ys <- readTVar writeT case ys of [] -> retry _ -> do let (z:zs) = reverse ys -- NB. lazy: we want the transaction to be -- short, otherwise it will conflict writeTVar writeT [] writeTVar readT (z:zs) pure z -- | A version of 'peekTQueue' which does not retry. Instead it -- returns @Nothing@ if no value is available. -- -- @since 1.0.0.0 tryPeekTQueue :: MonadSTM stm => TQueue stm a -> stm (Maybe a) tryPeekTQueue c = do m <- tryReadTQueue c case m of Nothing -> pure Nothing Just x -> do unGetTQueue c x pure m -- |Put a data item back onto a channel, where it will be the next item read. -- -- @since 1.0.0.0 unGetTQueue :: MonadSTM stm => TQueue stm a -> a -> stm () unGetTQueue (TQueue readT _) a = do xs <- readTVar readT writeTVar readT (a:xs) -- |Returns 'True' if the supplied 'TQueue' is empty. -- -- @since 1.0.0.0 isEmptyTQueue :: MonadSTM stm => TQueue stm a -> stm Bool isEmptyTQueue (TQueue readT writeT) = do xs <- readTVar readT case xs of (_:_) -> pure False [] -> null <$> readTVar writeT