-- | Transactional @CVar@s, for use with 'MonadSTM'. module Control.Concurrent.STM.CTMVar ( -- * @CTMVar@s CTMVar , newCTMVar , newEmptyCTMVar , takeCTMVar , putCTMVar , readCTMVar , tryTakeCTMVar , tryPutCTMVar , tryReadCTMVar , isEmptyCTMVar , swapCTMVar ) where import Control.Monad (liftM, when, unless) import Control.Monad.STM.Class import Data.Maybe (isJust, isNothing) -- | A @CTMVar@ is like an @MVar@ or a @CVar@, but using transactional -- memory. As transactions are atomic, this makes dealing with -- multiple @CTMVar@s easier than wrangling multiple @CVar@s. newtype CTMVar m a = CTMVar (CTVar m (Maybe a)) -- | Create a 'CTMVar' containing the given value. newCTMVar :: MonadSTM m => a -> m (CTMVar m a) newCTMVar a = do ctvar <- newCTVar $ Just a return $ CTMVar ctvar -- | Create a new empty 'CTMVar'. newEmptyCTMVar :: MonadSTM m => m (CTMVar m a) newEmptyCTMVar = do ctvar <- newCTVar Nothing return $ CTMVar ctvar -- | Take the contents of a 'CTMVar', or 'retry' if it is empty. takeCTMVar :: MonadSTM m => CTMVar m a -> m a takeCTMVar ctmvar = do taken <- tryTakeCTMVar ctmvar maybe retry return taken -- | Write to a 'CTMVar', or 'retry' if it is full. putCTMVar :: MonadSTM m => CTMVar m a -> a -> m () putCTMVar ctmvar a = do putted <- tryPutCTMVar ctmvar a unless putted retry -- | Read from a 'CTMVar' without emptying, or 'retry' if it is empty. readCTMVar :: MonadSTM m => CTMVar m a -> m a readCTMVar ctmvar = do readed <- tryReadCTMVar ctmvar maybe retry return readed -- | Try to take the contents of a 'CTMVar', returning 'Nothing' if it -- is empty. tryTakeCTMVar :: MonadSTM m => CTMVar m a -> m (Maybe a) tryTakeCTMVar (CTMVar ctvar) = do val <- readCTVar ctvar when (isJust val) $ writeCTVar ctvar Nothing return val -- | Try to write to a 'CTMVar', returning 'False' if it is full. tryPutCTMVar :: MonadSTM m => CTMVar m a -> a -> m Bool tryPutCTMVar (CTMVar ctvar) a = do val <- readCTVar ctvar when (isNothing val) $ writeCTVar ctvar (Just a) return $ isNothing val -- | Try to read from a 'CTMVar' without emptying, returning 'Nothing' -- if it is empty. tryReadCTMVar :: MonadSTM m => CTMVar m a -> m (Maybe a) tryReadCTMVar (CTMVar ctvar) = readCTVar ctvar -- | Check if a 'CTMVar' is empty or not. isEmptyCTMVar :: MonadSTM m => CTMVar m a -> m Bool isEmptyCTMVar ctmvar = isNothing `liftM` tryReadCTMVar ctmvar -- | Swap the contents of a 'CTMVar' returning the old contents, or -- 'retry' if it is empty. swapCTMVar :: MonadSTM m => CTMVar m a -> a -> m a swapCTMVar ctmvar a = do val <- takeCTMVar ctmvar putCTMVar ctmvar a return val