strict-stm-1.0.0.0: src/Control/Concurrent/Class/MonadSTM/Strict/TVar.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeOperators #-}
-- | This module corresponds to `Control.Concurrent.STM.TVar` in "stm" package
--
module Control.Concurrent.Class.MonadSTM.Strict.TVar
( -- * StrictTVar
StrictTVar
, LazyTVar
, toLazyTVar
, fromLazyTVar
, castStrictTVar
, newTVar
, newTVarIO
, newTVarWithInvariant
, newTVarWithInvariantIO
, readTVar
, readTVarIO
, writeTVar
, modifyTVar
, stateTVar
, swapTVar
, check
-- ** Low-level API
, checkInvariant
-- ** deprecated API
, updateTVar
, newTVarM
, newTVarWithInvariantM
-- * MonadLabelSTM
, labelTVar
, labelTVarIO
-- * MonadTraceSTM
, traceTVar
, traceTVarIO
) where
import qualified Control.Concurrent.Class.MonadSTM.TVar as Lazy
import Control.Monad.Class.MonadSTM hiding (traceTVar, traceTVarIO)
import GHC.Stack
type LazyTVar m = Lazy.TVar m
#if CHECK_TVAR_INVARIANT
data StrictTVar m a = StrictTVar
{ invariant :: !(a -> Maybe String)
-- ^ Invariant checked whenever updating the 'StrictTVar'.
, tvar :: !(LazyTVar m a)
}
#else
newtype StrictTVar m a = StrictTVar
{ tvar :: LazyTVar m a
}
#endif
labelTVar :: MonadLabelledSTM m => StrictTVar m a -> String -> STM m ()
labelTVar StrictTVar { tvar } = Lazy.labelTVar tvar
labelTVarIO :: MonadLabelledSTM m => StrictTVar m a -> String -> m ()
labelTVarIO v = atomically . labelTVar v
traceTVar :: MonadTraceSTM m
=> proxy m
-> StrictTVar m a
-> (Maybe a -> a -> InspectMonad m TraceValue)
-> STM m ()
traceTVar p StrictTVar {tvar} = Lazy.traceTVar p tvar
traceTVarIO :: MonadTraceSTM m
=> StrictTVar m a
-> (Maybe a -> a -> InspectMonad m TraceValue)
-> m ()
traceTVarIO StrictTVar {tvar} = Lazy.traceTVarIO tvar
castStrictTVar :: LazyTVar m ~ LazyTVar n
=> StrictTVar m a -> StrictTVar n a
castStrictTVar v@StrictTVar {tvar} =
mkStrictTVar (getInvariant v) tvar
-- | Get the underlying @TVar@
--
-- Since we obviously cannot guarantee that updates to this 'LazyTVar' will be
-- strict, this should be used with caution.
toLazyTVar :: StrictTVar m a -> LazyTVar m a
toLazyTVar StrictTVar { tvar } = tvar
fromLazyTVar :: LazyTVar m a -> StrictTVar m a
fromLazyTVar tvar =
#if CHECK_TVAR_INVARIANT
StrictTVar { invariant = const Nothing
, tvar
}
#else
StrictTVar { tvar }
#endif
newTVar :: MonadSTM m => a -> STM m (StrictTVar m a)
newTVar !a = (\tvar -> mkStrictTVar (const Nothing) tvar)
<$> Lazy.newTVar a
newTVarIO :: MonadSTM m => a -> m (StrictTVar m a)
newTVarIO = newTVarWithInvariantIO (const Nothing)
newTVarM :: MonadSTM m => a -> m (StrictTVar m a)
newTVarM = newTVarIO
{-# DEPRECATED newTVarM "Use newTVarIO" #-}
newTVarWithInvariant :: (MonadSTM m, HasCallStack)
=> (a -> Maybe String) -- ^ Invariant (expect 'Nothing')
-> a
-> STM m (StrictTVar m a)
newTVarWithInvariant invariant !a =
checkInvariant (invariant a) $
(\tvar -> mkStrictTVar invariant tvar)
<$> Lazy.newTVar a
newTVarWithInvariantIO :: (MonadSTM m, HasCallStack)
=> (a -> Maybe String) -- ^ Invariant (expect 'Nothing')
-> a
-> m (StrictTVar m a)
newTVarWithInvariantIO invariant !a =
checkInvariant (invariant a) $
(\tvar -> mkStrictTVar invariant tvar)
<$> Lazy.newTVarIO a
newTVarWithInvariantM :: (MonadSTM m, HasCallStack)
=> (a -> Maybe String) -- ^ Invariant (expect 'Nothing')
-> a
-> m (StrictTVar m a)
newTVarWithInvariantM = newTVarWithInvariantIO
{-# DEPRECATED newTVarWithInvariantM "Use newTVarWithInvariantIO" #-}
readTVar :: MonadSTM m => StrictTVar m a -> STM m a
readTVar StrictTVar { tvar } = Lazy.readTVar tvar
readTVarIO :: MonadSTM m => StrictTVar m a -> m a
readTVarIO StrictTVar { tvar } = Lazy.readTVarIO tvar
writeTVar :: (MonadSTM m, HasCallStack) => StrictTVar m a -> a -> STM m ()
writeTVar v !a =
checkInvariant (getInvariant v a) $
Lazy.writeTVar (tvar v) a
modifyTVar :: MonadSTM m => StrictTVar m a -> (a -> a) -> STM m ()
modifyTVar v f = readTVar v >>= writeTVar v . f
stateTVar :: MonadSTM m => StrictTVar m s -> (s -> (a, s)) -> STM m a
stateTVar v f = do
a <- readTVar v
let (b, a') = f a
writeTVar v a'
return b
swapTVar :: MonadSTM m => StrictTVar m a -> a -> STM m a
swapTVar v a' = do
a <- readTVar v
writeTVar v a'
return a
updateTVar :: MonadSTM m => StrictTVar m s -> (s -> (a, s)) -> STM m a
updateTVar = stateTVar
{-# DEPRECATED updateTVar "Use stateTVar" #-}
{-------------------------------------------------------------------------------
Dealing with invariants
-------------------------------------------------------------------------------}
getInvariant :: StrictTVar m a -> a -> Maybe String
mkStrictTVar :: (a -> Maybe String) -> Lazy.TVar m a -> StrictTVar m a
-- | Check invariant (if enabled) before continuing
--
-- @checkInvariant mErr x@ is equal to @x@ if @mErr == Nothing@, and throws
-- an error @err@ if @mErr == Just err@.
--
-- This is exported so that other code that wants to conditionally check
-- invariants can reuse the same logic, rather than having to introduce new
-- per-package flags.
checkInvariant :: HasCallStack => Maybe String -> a -> a
#if CHECK_TVAR_INVARIANT
getInvariant StrictTVar {invariant} = invariant
mkStrictTVar invariant tvar = StrictTVar {invariant, tvar}
checkInvariant Nothing k = k
checkInvariant (Just err) _ = error $ "Invariant violation: " ++ err
#else
getInvariant _ = \_ -> Nothing
mkStrictTVar _invariant tvar = StrictTVar {tvar}
checkInvariant _err k = k
#endif