{-# LANGUAGE CPP                #-}
{-# LANGUAGE DeriveDataTypeable #-}

#if __GLASGOW_HASKELL__ >= 701
{-# LANGUAGE Trustworthy        #-}
#endif

-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Concurrent.STM.TBQueue
-- Copyright   :  (c) The University of Glasgow 2012
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  libraries@haskell.org
-- Stability   :  experimental
-- Portability :  non-portable (requires STM)
--
-- 'TBQueue' is a bounded version of 'TQueue'. The queue has a maximum
-- capacity set when it is created.  If the queue already contains the
-- maximum number of elements, then 'writeTBQueue' blocks until an
-- element is removed from the queue.
--
-- The implementation is based on the traditional purely-functional
-- queue representation that uses two lists to obtain amortised /O(1)/
-- enqueue and dequeue operations.
--
-- @since 2.4
-----------------------------------------------------------------------------

module Control.Concurrent.STM.TBQueue (
        -- * TBQueue
        TBQueue,
        newTBQueue,
        newTBQueueIO,
        readTBQueue,
        tryReadTBQueue,
        flushTBQueue,
        peekTBQueue,
        tryPeekTBQueue,
        writeTBQueue,
        unGetTBQueue,
        lengthTBQueue,
        isEmptyTBQueue,
        isFullTBQueue,
  ) where

import           Data.Typeable   (Typeable)
import           GHC.Conc        (STM, TVar, newTVar, newTVarIO, orElse,
                                  readTVar, retry, writeTVar)
import           Numeric.Natural (Natural)
import           Prelude         hiding (read)

-- | 'TBQueue' is an abstract type representing a bounded FIFO channel.
--
-- @since 2.4
data TBQueue a
   = TBQueue {-# UNPACK #-} !(TVar Natural) -- CR:  read capacity
             {-# UNPACK #-} !(TVar [a])     -- R:   elements waiting to be read
             {-# UNPACK #-} !(TVar Natural) -- CW:  write capacity
             {-# UNPACK #-} !(TVar [a])     -- W:   elements written (head is most recent)
                            !(Natural)      -- CAP: initial capacity
  deriving Typeable

instance Eq (TBQueue a) where
  TBQueue TVar Natural
a TVar [a]
_ TVar Natural
_ TVar [a]
_ Natural
_ == :: TBQueue a -> TBQueue a -> Bool
== TBQueue TVar Natural
b TVar [a]
_ TVar Natural
_ TVar [a]
_ Natural
_ = TVar Natural
a TVar Natural -> TVar Natural -> Bool
forall a. Eq a => a -> a -> Bool
== TVar Natural
b

-- Total channel capacity remaining is CR + CW. Reads only need to
-- access CR, writes usually need to access only CW but sometimes need
-- CR.  So in the common case we avoid contention between CR and CW.
--
--   - when removing an element from R:
--     CR := CR + 1
--
--   - when adding an element to W:
--     if CW is non-zero
--         then CW := CW - 1
--         then if CR is non-zero
--                 then CW := CR - 1; CR := 0
--                 else **FULL**

-- | Builds and returns a new instance of 'TBQueue'.
newTBQueue :: Natural   -- ^ maximum number of elements the queue can hold
           -> STM (TBQueue a)
newTBQueue :: forall a. Natural -> STM (TBQueue a)
newTBQueue Natural
size = do
  TVar [a]
read  <- [a] -> STM (TVar [a])
forall a. a -> STM (TVar a)
newTVar []
  TVar [a]
write <- [a] -> STM (TVar [a])
forall a. a -> STM (TVar a)
newTVar []
  TVar Natural
rsize <- Natural -> STM (TVar Natural)
forall a. a -> STM (TVar a)
newTVar Natural
0
  TVar Natural
wsize <- Natural -> STM (TVar Natural)
forall a. a -> STM (TVar a)
newTVar Natural
size
  TBQueue a -> STM (TBQueue a)
forall (m :: * -> *) a. Monad m => a -> m a
return (TVar Natural
-> TVar [a] -> TVar Natural -> TVar [a] -> Natural -> TBQueue a
forall a.
TVar Natural
-> TVar [a] -> TVar Natural -> TVar [a] -> Natural -> TBQueue a
TBQueue TVar Natural
rsize TVar [a]
read TVar Natural
wsize TVar [a]
write Natural
size)

-- |@IO@ version of 'newTBQueue'.  This is useful for creating top-level
-- 'TBQueue's using 'System.IO.Unsafe.unsafePerformIO', because using
-- 'atomically' inside 'System.IO.Unsafe.unsafePerformIO' isn't
-- possible.
newTBQueueIO :: Natural -> IO (TBQueue a)
newTBQueueIO :: forall a. Natural -> IO (TBQueue a)
newTBQueueIO Natural
size = do
  TVar [a]
read  <- [a] -> IO (TVar [a])
forall a. a -> IO (TVar a)
newTVarIO []
  TVar [a]
write <- [a] -> IO (TVar [a])
forall a. a -> IO (TVar a)
newTVarIO []
  TVar Natural
rsize <- Natural -> IO (TVar Natural)
forall a. a -> IO (TVar a)
newTVarIO Natural
0
  TVar Natural
wsize <- Natural -> IO (TVar Natural)
forall a. a -> IO (TVar a)
newTVarIO Natural
size
  TBQueue a -> IO (TBQueue a)
forall (m :: * -> *) a. Monad m => a -> m a
return (TVar Natural
-> TVar [a] -> TVar Natural -> TVar [a] -> Natural -> TBQueue a
forall a.
TVar Natural
-> TVar [a] -> TVar Natural -> TVar [a] -> Natural -> TBQueue a
TBQueue TVar Natural
rsize TVar [a]
read TVar Natural
wsize TVar [a]
write Natural
size)

-- |Write a value to a 'TBQueue'; blocks if the queue is full.
writeTBQueue :: TBQueue a -> a -> STM ()
writeTBQueue :: forall a. TBQueue a -> a -> STM ()
writeTBQueue (TBQueue TVar Natural
rsize TVar [a]
_read TVar Natural
wsize TVar [a]
write Natural
_size) a
a = do
  Natural
w <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
wsize
  if (Natural
w Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
     then do TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
wsize (Natural -> STM ()) -> Natural -> STM ()
forall a b. (a -> b) -> a -> b
$! Natural
w Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
1
     else do
          Natural
r <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
rsize
          if (Natural
r Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
             then do TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
rsize Natural
0
                     TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
wsize (Natural -> STM ()) -> Natural -> STM ()
forall a b. (a -> b) -> a -> b
$! Natural
r Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
1
             else STM ()
forall a. STM a
retry
  [a]
listend <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
write
  TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
write (a
aa -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
listend)

-- |Read the next value from the 'TBQueue'.
readTBQueue :: TBQueue a -> STM a
readTBQueue :: forall a. TBQueue a -> STM a
readTBQueue (TBQueue TVar Natural
rsize TVar [a]
read TVar Natural
_wsize TVar [a]
write Natural
_size) = do
  [a]
xs <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
read
  Natural
r <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
rsize
  TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
rsize (Natural -> STM ()) -> Natural -> STM ()
forall a b. (a -> b) -> a -> b
$! Natural
r Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
+ Natural
1
  case [a]
xs of
    (a
x:[a]
xs') -> do
      TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
read [a]
xs'
      a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x
    [] -> do
      [a]
ys <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
write
      case [a]
ys of
        [] -> STM a
forall a. STM a
retry
        [a]
_  -> do
          let (a
z:[a]
zs) = [a] -> [a]
forall a. [a] -> [a]
reverse [a]
ys -- NB. lazy: we want the transaction to be
                                  -- short, otherwise it will conflict
          TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
write []
          TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
read [a]
zs
          a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return a
z

-- | A version of 'readTBQueue' which does not retry. Instead it
-- returns @Nothing@ if no value is available.
tryReadTBQueue :: TBQueue a -> STM (Maybe a)
tryReadTBQueue :: forall a. TBQueue a -> STM (Maybe a)
tryReadTBQueue TBQueue a
c = (a -> Maybe a) -> STM a -> STM (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just (TBQueue a -> STM a
forall a. TBQueue a -> STM a
readTBQueue TBQueue a
c) STM (Maybe a) -> STM (Maybe a) -> STM (Maybe a)
forall a. STM a -> STM a -> STM a
`orElse` Maybe a -> STM (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing

-- | Efficiently read the entire contents of a 'TBQueue' into a list. This
-- function never retries.
--
-- @since 2.4.5
flushTBQueue :: TBQueue a -> STM [a]
flushTBQueue :: forall a. TBQueue a -> STM [a]
flushTBQueue (TBQueue TVar Natural
rsize TVar [a]
read TVar Natural
wsize TVar [a]
write Natural
size) = do
  [a]
xs <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
read
  [a]
ys <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
write
  if [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
xs Bool -> Bool -> Bool
&& [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
ys
    then [a] -> STM [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
    else do
      TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
read []
      TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
write []
      TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
rsize Natural
0
      TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
wsize Natural
size
      [a] -> STM [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([a]
xs [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [a] -> [a]
forall a. [a] -> [a]
reverse [a]
ys)

-- | Get the next value from the @TBQueue@ without removing it,
-- retrying if the channel is empty.
peekTBQueue :: TBQueue a -> STM a
peekTBQueue :: forall a. TBQueue a -> STM a
peekTBQueue (TBQueue TVar Natural
_ TVar [a]
read TVar Natural
_ TVar [a]
write Natural
_) = do
  [a]
xs <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
read
  case [a]
xs of
    (a
x:[a]
_) -> a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x
    [] -> do
      [a]
ys <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
write
      case [a]
ys of
        [] -> STM a
forall a. STM a
retry
        [a]
_  -> do
          let (a
z:[a]
zs) = [a] -> [a]
forall a. [a] -> [a]
reverse [a]
ys -- NB. lazy: we want the transaction to be
                                  -- short, otherwise it will conflict
          TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
write []
          TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
read (a
za -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
zs)
          a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return a
z

-- | A version of 'peekTBQueue' which does not retry. Instead it
-- returns @Nothing@ if no value is available.
tryPeekTBQueue :: TBQueue a -> STM (Maybe a)
tryPeekTBQueue :: forall a. TBQueue a -> STM (Maybe a)
tryPeekTBQueue TBQueue a
c = do
  Maybe a
m <- TBQueue a -> STM (Maybe a)
forall a. TBQueue a -> STM (Maybe a)
tryReadTBQueue TBQueue a
c
  case Maybe a
m of
    Maybe a
Nothing -> Maybe a -> STM (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
    Just a
x  -> do
      TBQueue a -> a -> STM ()
forall a. TBQueue a -> a -> STM ()
unGetTBQueue TBQueue a
c a
x
      Maybe a -> STM (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
m

-- |Put a data item back onto a channel, where it will be the next item read.
-- Blocks if the queue is full.
unGetTBQueue :: TBQueue a -> a -> STM ()
unGetTBQueue :: forall a. TBQueue a -> a -> STM ()
unGetTBQueue (TBQueue TVar Natural
rsize TVar [a]
read TVar Natural
wsize TVar [a]
_write Natural
_size) a
a = do
  Natural
r <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
rsize
  if (Natural
r Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
     then do TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
rsize (Natural -> STM ()) -> Natural -> STM ()
forall a b. (a -> b) -> a -> b
$! Natural
r Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
1
     else do
          Natural
w <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
wsize
          if (Natural
w Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
             then TVar Natural -> Natural -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Natural
wsize (Natural -> STM ()) -> Natural -> STM ()
forall a b. (a -> b) -> a -> b
$! Natural
w Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
1
             else STM ()
forall a. STM a
retry
  [a]
xs <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
read
  TVar [a] -> [a] -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar [a]
read (a
aa -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
xs)

-- |Return the length of a 'TBQueue'.
--
-- @since 2.5.0.0
lengthTBQueue :: TBQueue a -> STM Natural
lengthTBQueue :: forall a. TBQueue a -> STM Natural
lengthTBQueue (TBQueue TVar Natural
rsize TVar [a]
_read TVar Natural
wsize TVar [a]
_write Natural
size) = do
  Natural
r <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
rsize
  Natural
w <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
wsize
  Natural -> STM Natural
forall (m :: * -> *) a. Monad m => a -> m a
return (Natural -> STM Natural) -> Natural -> STM Natural
forall a b. (a -> b) -> a -> b
$! Natural
size Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
r Natural -> Natural -> Natural
forall a. Num a => a -> a -> a
- Natural
w

-- |Returns 'True' if the supplied 'TBQueue' is empty.
isEmptyTBQueue :: TBQueue a -> STM Bool
isEmptyTBQueue :: forall a. TBQueue a -> STM Bool
isEmptyTBQueue (TBQueue TVar Natural
_rsize TVar [a]
read TVar Natural
_wsize TVar [a]
write Natural
_size) = do
  [a]
xs <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
read
  case [a]
xs of
    (a
_:[a]
_) -> Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
    [] -> do [a]
ys <- TVar [a] -> STM [a]
forall a. TVar a -> STM a
readTVar TVar [a]
write
             case [a]
ys of
               [] -> Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
               [a]
_  -> Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False

-- |Returns 'True' if the supplied 'TBQueue' is full.
--
-- @since 2.4.3
isFullTBQueue :: TBQueue a -> STM Bool
isFullTBQueue :: forall a. TBQueue a -> STM Bool
isFullTBQueue (TBQueue TVar Natural
rsize TVar [a]
_read TVar Natural
wsize TVar [a]
_write Natural
_size) = do
  Natural
w <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
wsize
  if (Natural
w Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
     then Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
     else do
         Natural
r <- TVar Natural -> STM Natural
forall a. TVar a -> STM a
readTVar TVar Natural
rsize
         if (Natural
r Natural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
> Natural
0)
            then Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
            else Bool -> STM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True