module Test.DejaFu.STM
(
STMLike
, STMST
, STMIO
, Result(..)
, runTransaction
, runTransactionST
, runTransactionIO
, retry
, orElse
, check
, throwSTM
, catchSTM
, CTVar
, CTVarId
, newCTVar
, readCTVar
, writeCTVar
) where
import Control.Exception (Exception, SomeException(..))
import Control.Monad (liftM)
import Control.Monad.Catch (MonadCatch(..), MonadThrow(..))
import Control.Monad.Cont (cont)
import Control.Monad.ST (ST, runST)
import Data.IORef (IORef)
import Data.STRef (STRef)
import Test.DejaFu.Internal
import Test.DejaFu.STM.Internal
import qualified Control.Monad.STM.Class as C
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative (Applicative)
#endif
newtype STMLike t n r a = S { unS :: M t n r a } deriving (Functor, Applicative, Monad)
type STMST t a = STMLike t (ST t) (STRef t) a
type STMIO t a = STMLike t IO IORef a
instance MonadThrow (STMLike t n r) where
throwM = throwSTM
instance MonadCatch (STMLike t n r) where
catch = catchSTM
instance Monad n => C.MonadSTM (STMLike t n r) where
type CTVar (STMLike t n r) = CTVar t r
retry = retry
orElse = orElse
newCTVar = newCTVar
readCTVar = readCTVar
writeCTVar = writeCTVar
retry :: Monad n => STMLike t n r a
retry = S $ cont $ const ARetry
orElse :: Monad n => STMLike t n r a -> STMLike t n r a -> STMLike t n r a
orElse a b = S $ cont $ AOrElse (unS a) (unS b)
check :: Monad n => Bool -> STMLike t n r ()
check = C.check
throwSTM :: Exception e => e -> STMLike t n r a
throwSTM e = S $ cont $ const $ AThrow (SomeException e)
catchSTM :: Exception e => STMLike t n r a -> (e -> STMLike t n r a) -> STMLike t n r a
catchSTM stm handler = S $ cont $ ACatch (unS stm) (unS . handler)
newCTVar :: Monad n => a -> STMLike t n r (CTVar t r a)
newCTVar a = S $ cont lifted where
lifted c = ANew $ \ref ctvid -> c `liftM` newCTVar' ref ctvid
newCTVar' ref ctvid = (\r -> V (ctvid, r)) `liftM` newRef ref a
readCTVar :: Monad n => CTVar t r a -> STMLike t n r a
readCTVar ctvar = S $ cont $ ARead ctvar
writeCTVar :: Monad n => CTVar t r a -> a -> STMLike t n r ()
writeCTVar ctvar a = S $ cont $ \c -> AWrite ctvar a $ c ()
runTransaction :: (forall t. STMST t a) -> Result a
runTransaction ma = fst $ runST $ runTransactionST ma 0
runTransactionST :: STMST t a -> CTVarId -> ST t (Result a, CTVarId)
runTransactionST = runTransactionM fixedST where
fixedST = refST $ \mb -> cont (\c -> ALift $ c `liftM` mb)
runTransactionIO :: STMIO t a -> CTVarId -> IO (Result a, CTVarId)
runTransactionIO = runTransactionM fixedIO where
fixedIO = refIO $ \mb -> cont (\c -> ALift $ c `liftM` mb)
runTransactionM :: Monad n
=> Fixed t n r -> STMLike t n r a -> CTVarId -> n (Result a, CTVarId)
runTransactionM ref ma ctvid = do
(res, undo, ctvid') <- doTransaction ref (unS ma) ctvid
case res of
Success _ _ _ -> return (res, ctvid')
_ -> undo >> return (res, ctvid)