{-# LANGUAGE ExistentialQuantification, FlexibleContexts, TypeOperators #-}
module Test.IOSpec.STM
(
STMS
, atomically
, STM
, TVar
, newTVar
, readTVar
, writeTVar
, retry
, orElse
, check
)
where
import Test.IOSpec.VirtualMachine
import Test.IOSpec.Types
import Data.Dynamic
import Data.Maybe (fromJust)
import Control.Monad.State
import Control.Monad (ap)
data STMS a =
forall b . Atomically (STM b) (b -> a)
instance Functor STMS where
fmap f (Atomically s io) = Atomically s (f . io)
atomically :: (STMS :<: f) => STM a -> IOSpec f a
atomically stm = inject $ Atomically stm (return)
instance Executable STMS where
step (Atomically stm b) =
do state <- get
case runStateT (executeSTM stm) state of
Done (Nothing,_) -> return Block
Done (Just x,finalState) -> put finalState >> return (Step (b x))
_ -> internalError "Unsafe usage of STM"
data STM a =
STMReturn a
| NewTVar Data (Loc -> STM a)
| ReadTVar Loc (Data -> STM a)
| WriteTVar Loc Data (STM a)
| Retry
| OrElse (STM a) (STM a)
instance Functor STM where
fmap f (STMReturn x) = STMReturn (f x)
fmap f (NewTVar d io) = NewTVar d (fmap f . io)
fmap f (ReadTVar l io) = ReadTVar l (fmap f . io)
fmap f (WriteTVar l d io) = WriteTVar l d (fmap f io)
fmap _ Retry = Retry
fmap f (OrElse io1 io2) = OrElse (fmap f io1) (fmap f io2)
instance Applicative STM where
pure = STMReturn
(<*>) = ap
instance Monad STM where
return = STMReturn
STMReturn a >>= f = f a
NewTVar d g >>= f = NewTVar d (\l -> g l >>= f)
ReadTVar l g >>= f = ReadTVar l (\d -> g d >>= f)
WriteTVar l d p >>= f = WriteTVar l d (p >>= f)
Retry >>= _ = Retry
OrElse p q >>= f = OrElse (p >>= f) (q >>= f)
newtype TVar a = TVar Loc
newTVar :: Typeable a => a -> STM (TVar a)
newTVar d = NewTVar (toDyn d) (STMReturn . TVar)
readTVar :: Typeable a => TVar a -> STM a
readTVar (TVar l) = ReadTVar l (STMReturn . fromJust . fromDynamic)
writeTVar :: Typeable a => TVar a -> a -> STM ()
writeTVar (TVar l) d = WriteTVar l (toDyn d) (STMReturn ())
retry :: STM a
retry = Retry
check :: Bool -> STM ()
check True = return ()
check False = retry
orElse :: STM a -> STM a -> STM a
orElse p q = OrElse p q
executeSTM :: STM a -> VM (Maybe a)
executeSTM (STMReturn x) = return (return x)
executeSTM (NewTVar d io) = do
loc <- alloc
updateHeap loc d
executeSTM (io loc)
executeSTM (ReadTVar l io) = do
lookupHeap l >>= \(Just d) -> do
executeSTM (io d)
executeSTM (WriteTVar l d io) = do
updateHeap l d
executeSTM io
executeSTM Retry = return Nothing
executeSTM (OrElse p q) = do
state <- get
case runStateT (executeSTM p) state of
Done (Nothing,_) -> executeSTM q
Done (Just x,s) -> put s >> return (Just x)
_ -> internalError "Unsafe usage of STM"
internalError :: String -> a
internalError msg = error ("IOSpec.STM: " ++ msg)