    module Brick.BChan
      ( BChan
      , newBChan
      , writeBChan
      , writeBChanNonBlocking
      , readBChan
      , readBChan2
      )
    where

    #if !MIN_VERSION_base(4,8,0)
    import Control.Applicative ((<$>))
    #endif

    import Control.Concurrent.STM.TBQueue
    import Control.Monad.STM (atomically, orElse)

    -- | @BChan@ is an abstract type representing a bounded FIFO channel.
    data BChan a = BChan (TBQueue a)

    -- | Builds and returns a new instance of @BChan@.
    newBChan :: Int   -- ^ maximum number of elements the channel can hold
              -> IO (BChan a)
    newBChan :: Int -> IO (BChan a)
    newBChan size :: Int
    size = STM (BChan a) -> IO (BChan a)
    forall a. STM a -> IO a
    atomically (STM (BChan a) -> IO (BChan a)) -> STM (BChan a) -> IO (BChan a)
    forall a b. (a -> b) -> a -> b
    $ TBQueue a -> BChan a
    forall a. TBQueue a -> BChan a
    BChan (TBQueue a -> BChan a) -> STM (TBQueue a) -> STM (BChan a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Natural -> STM (TBQueue a)
    forall a. Natural -> STM (TBQueue a)
    newTBQueue (Int -> Natural
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    size)

    -- | Writes a value to a @BChan@; blocks if the channel is full.
    writeBChan :: BChan a -> a -> IO ()
    writeBChan :: BChan a -> a -> IO ()
    writeBChan (BChan q :: TBQueue a
    q) a :: a
    a = STM () -> IO ()
    forall a. STM a -> IO a
    atomically (STM () -> IO ()) -> STM () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ TBQueue a -> a -> STM ()
    forall a. TBQueue a -> a -> STM ()
    writeTBQueue TBQueue a
    q a
    a

    -- | Attempts to write a value to a @BChan@. If the channel has room,
    -- the value is written and this returns 'True'. Otherwise this returns
    -- 'False' and returns immediately.
    writeBChanNonBlocking :: BChan a -> a -> IO Bool
    writeBChanNonBlocking :: BChan a -> a -> IO Bool
    writeBChanNonBlocking (BChan q :: TBQueue a
    q) a :: a
    a = STM Bool -> IO Bool
    forall a. STM a -> IO a
    atomically (STM Bool -> IO Bool) -> STM Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ do
        Bool
    f <- TBQueue a -> STM Bool
    forall a. TBQueue a -> STM Bool
    isFullTBQueue TBQueue a
    q
        if Bool
    f
           then Bool -> STM Bool
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False
           else TBQueue a -> a -> STM ()
    forall a. TBQueue a -> a -> STM ()
    writeTBQueue TBQueue a
    q a
    a STM () -> STM Bool -> STM Bool
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Bool -> STM Bool
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True

    -- | Reads the next value from the @BChan@; blocks if necessary.
    readBChan :: BChan a -> IO a
    readBChan :: BChan a -> IO a
    readBChan (BChan q :: TBQueue a
    q) = STM a -> IO a
    forall a. STM a -> IO a
    atomically (STM a -> IO a) -> STM a -> IO a
    forall a b. (a -> b) -> a -> b
    $ TBQueue a -> STM a
    forall a. TBQueue a -> STM a
    readTBQueue TBQueue a
    q

    -- | Reads the next value from either @BChan@, prioritizing the first
    -- @BChan@; blocks if necessary.
    readBChan2 :: BChan a -> BChan b -> IO (Either a b)
    readBChan2 :: BChan a -> BChan b -> IO (Either a b)
    readBChan2 (BChan q1 :: TBQueue a
    q1) (BChan q2 :: TBQueue b
    q2) = STM (Either a b) -> IO (Either a b)
    forall a. STM a -> IO a
    atomically (STM (Either a b) -> IO (Either a b))
    -> STM (Either a b) -> IO (Either a b)
    forall a b. (a -> b) -> a -> b
    $
      (a -> Either a b
    forall a b. a -> Either a b
    Left (a -> Either a b) -> STM a -> STM (Either a b)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> TBQueue a -> STM a
    forall a. TBQueue a -> STM a
    readTBQueue TBQueue a
    q1) STM (Either a b) -> STM (Either a b) -> STM (Either a b)
    forall a. STM a -> STM a -> STM a
    `orElse` (b -> Either a b
    forall a b. b -> Either a b
    Right (b -> Either a b) -> STM b -> STM (Either a b)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> TBQueue b -> STM b
    forall a. TBQueue a -> STM a
    readTBQueue TBQueue b
    q2)