mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
70 lines
1.7 KiB
Haskell
70 lines
1.7 KiB
Haskell
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
{-|
|
|
Module: Internal.TCloseQ
|
|
Description: Transactional queues with a close operation.
|
|
|
|
This module provides a thin layer over 'TQueue', which affords "closing"
|
|
queues. Reading from a closed queue returns 'Nothing'.
|
|
-}
|
|
module Internal.TCloseQ
|
|
( Q
|
|
, ErrClosed(..)
|
|
, new
|
|
, read
|
|
, write
|
|
, close
|
|
) where
|
|
|
|
import Prelude hiding (read)
|
|
|
|
import Control.Concurrent.STM
|
|
|
|
import Control.Exception (Exception)
|
|
import Control.Monad (unless, when)
|
|
import Data.Maybe (isNothing)
|
|
|
|
-- | A Queue with a close operation, with element type @a@.
|
|
data Q a = Q
|
|
{ q :: TQueue (Maybe a)
|
|
, isClosed :: TVar Bool
|
|
}
|
|
|
|
-- | An exception which is thrown if a caller tries to write to a closed queue.
|
|
data ErrClosed = ErrClosed
|
|
deriving(Show)
|
|
instance Exception ErrClosed
|
|
|
|
-- | Create a new empty queue.
|
|
new :: STM (Q a)
|
|
new = do
|
|
q <- newTQueue
|
|
isClosed <- newTVar False
|
|
pure Q{..}
|
|
|
|
-- | Read a value from the queue. Returns Nothing if the queue is closed.
|
|
read :: Q a -> STM (Maybe a)
|
|
read Q{q} = do
|
|
ret <- readTQueue q
|
|
when (isNothing ret) $
|
|
-- put it back in, so future reads also return nothing:
|
|
writeTQueue q ret
|
|
pure ret
|
|
|
|
-- | Write a value to the queue, which must not be closed. If it is closed,
|
|
-- this will throw 'ErrClosed'.
|
|
write :: Q a -> a -> STM ()
|
|
write Q{q, isClosed} v = do
|
|
c <- readTVar isClosed
|
|
when c $ throwSTM ErrClosed
|
|
writeTQueue q (Just v)
|
|
|
|
-- | Close a queue. It is safe to close a queue more than once; subsequent
|
|
-- closes will have no effect.
|
|
close :: Q a -> STM ()
|
|
close Q{q, isClosed} = do
|
|
c <- readTVar isClosed
|
|
unless c $ do
|
|
writeTVar isClosed True
|
|
writeTQueue q Nothing
|