Make Test.DejaFu.STM an internal module

This commit is contained in:
Michael Walker 2017-12-06 21:19:35 +00:00
parent 81bcb5a351
commit 4ae16eab64
7 changed files with 88 additions and 193 deletions

View File

@ -84,6 +84,10 @@ This project is versioned according to the [Package Versioning Policy](https://p
It is no longer possible to test things in `ST`.
### Test.DejaFu.STM
- This is now an internal module. (#155)
### Miscellaneous
- The minimum supported version of concurrency is now 1.3.0.0.

View File

@ -58,8 +58,8 @@ import Test.DejaFu.Schedule
import qualified Control.Monad.Conc.Class as C
import Test.DejaFu.Conc.Internal
import Test.DejaFu.Conc.Internal.Common
import Test.DejaFu.Conc.Internal.STM
import Test.DejaFu.Internal
import Test.DejaFu.STM
import Test.DejaFu.Types
import Test.DejaFu.Utils
@ -120,7 +120,7 @@ instance Monad n => C.MonadConc (ConcT r n) where
type MVar (ConcT r n) = MVar r
type CRef (ConcT r n) = CRef r
type Ticket (ConcT r n) = Ticket
type STM (ConcT r n) = STMLike n r
type STM (ConcT r n) = S n r
type ThreadId (ConcT r n) = ThreadId
-- ----------

View File

@ -31,11 +31,10 @@ import qualified Data.Sequence as Seq
import Test.DejaFu.Conc.Internal.Common
import Test.DejaFu.Conc.Internal.Memory
import Test.DejaFu.Conc.Internal.STM
import Test.DejaFu.Conc.Internal.Threading
import Test.DejaFu.Internal
import Test.DejaFu.Schedule
import Test.DejaFu.STM (Result(..),
runTransaction)
import Test.DejaFu.Types
--------------------------------------------------------------------------------

View File

@ -12,15 +12,16 @@
--
-- Common types and utility functions for deterministic execution of
-- 'MonadConc' implementations. This module is NOT considered to form
-- part of the public interface of this library.
module Test.DejaFu.Conc.Internal.Common where
import Control.Exception (Exception, MaskingState(..))
import Data.Map.Strict (Map)
import Test.DejaFu.STM (STMLike)
import Control.Exception (Exception, MaskingState(..))
import Data.Map.Strict (Map)
import Test.DejaFu.Conc.Internal.STM (S)
import Test.DejaFu.Types
#if MIN_VERSION_base(4,9,0)
import qualified Control.Monad.Fail as Fail
import qualified Control.Monad.Fail as Fail
#endif
--------------------------------------------------------------------------------
@ -140,7 +141,7 @@ data Action n r =
| forall a. AMasking MaskingState ((forall b. M n r b -> M n r b) -> M n r a) (a -> Action n r)
| AResetMask Bool Bool MaskingState (Action n r)
| forall a. AAtom (STMLike n r a) (a -> Action n r)
| forall a. AAtom (S n r a) (a -> Action n r)
| ALift (n (Action n r))
| AYield (Action n r)
| ADelay Int (Action n r)

View File

@ -1,70 +1,88 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
-- Must come after TypeFamilies
{-# LANGUAGE NoMonoLocalBinds #-}
-- |
-- Module : Test.DejaFu.STM.Internal
-- Copyright : (c) 2016 Michael Walker
-- Module : Test.DejaFu.Conc.Internal.STM
-- Copyright : (c) 2017 Michael Walker
-- License : MIT
-- Maintainer : Michael Walker <mike@barrucadu.co.uk>
-- Stability : experimental
-- Portability : CPP, ExistentialQuantification, MultiParamTypeClasses, RankNTypes
-- Portability : CPP, ExistentialQuantification, MultiParamTypeClasses, NoMonoLocalBinds, TypeFamilies
--
-- 'MonadSTM' testing implementation, internal types and
-- definitions. This module is NOT considered to form part of the
-- public interface of this library.
module Test.DejaFu.STM.Internal where
-- 'MonadSTM' testing implementation, internal types and definitions.
-- This module is NOT considered to form part of the public interface
-- of this library.
module Test.DejaFu.Conc.Internal.STM where
import Control.DeepSeq (NFData(..))
import Control.Exception (Exception, SomeException, fromException,
toException)
import Control.Monad.Ref (MonadRef, newRef, readRef, writeRef)
import Data.List (nub)
import Control.Applicative (Alternative(..))
import Control.Exception (Exception, SomeException,
fromException, toException)
import Control.Monad (MonadPlus(..))
import Control.Monad.Catch (MonadCatch(..), MonadThrow(..))
import Control.Monad.Ref (MonadRef, newRef, readRef, writeRef)
import Data.List (nub)
import qualified Control.Monad.STM.Class as C
import Test.DejaFu.Internal
import Test.DejaFu.Types
#if MIN_VERSION_base(4,9,0)
import qualified Control.Monad.Fail as Fail
import qualified Control.Monad.Fail as Fail
#endif
--------------------------------------------------------------------------------
-- The @STMLike@ monad
-- * The @S@ monad
-- | The underlying monad is based on continuations over primitive
-- actions.
--
-- This is not @Cont@ because we want to give it a custom @MonadFail@
-- instance.
newtype M n r a = M { runM :: (a -> STMAction n r) -> STMAction n r }
newtype S n r a = S { runSTM :: (a -> STMAction n r) -> STMAction n r }
instance Functor (M n r) where
fmap f m = M $ \ c -> runM m (c . f)
instance Functor (S n r) where
fmap f m = S $ \c -> runSTM m (c . f)
instance Applicative (M n r) where
pure x = M $ \c -> c x
f <*> v = M $ \c -> runM f (\g -> runM v (c . g))
instance Applicative (S n r) where
pure x = S $ \c -> c x
f <*> v = S $ \c -> runSTM f (\g -> runSTM v (c . g))
instance Monad (M n r) where
instance Monad (S n r) where
return = pure
m >>= k = M $ \c -> runM m (\x -> runM (k x) c)
m >>= k = S $ \c -> runSTM m (\x -> runSTM (k x) c)
#if MIN_VERSION_base(4,9,0)
fail = Fail.fail
-- | @since 0.7.1.2
instance Fail.MonadFail (M n r) where
instance Fail.MonadFail (S n r) where
#endif
fail e = cont (\_ -> SThrow (MonadFailException e))
fail e = S $ \_ -> SThrow (MonadFailException e)
-- | Construct a continuation-passing operation from a function.
cont :: ((a -> STMAction n r) -> STMAction n r) -> M n r a
cont = M
instance MonadThrow (S n r) where
throwM e = S $ \_ -> SThrow e
-- | Run a CPS computation with the given final computation.
runCont :: M n r a -> (a -> STMAction n r) -> STMAction n r
runCont = runM
instance MonadCatch (S n r) where
catch stm handler = S $ SCatch handler stm
instance Alternative (S n r) where
a <|> b = S $ SOrElse a b
empty = S $ const SRetry
instance MonadPlus (S n r)
instance C.MonadSTM (S n r) where
type TVar (S n r) = TVar r
newTVarN n = S . SNew n
readTVar = S . SRead
writeTVar tvar a = S $ \c -> SWrite tvar a (c ())
--------------------------------------------------------------------------------
-- * Primitive actions
@ -72,10 +90,10 @@ runCont = runM
-- | STM transactions are represented as a sequence of primitive
-- actions.
data STMAction n r
= forall a e. Exception e => SCatch (e -> M n r a) (M n r a) (a -> STMAction n r)
= forall a e. Exception e => SCatch (e -> S n r a) (S n r a) (a -> STMAction n r)
| forall a. SRead (TVar r a) (a -> STMAction n r)
| forall a. SWrite (TVar r a) a (STMAction n r)
| forall a. SOrElse (M n r a) (M n r a) (a -> STMAction n r)
| forall a. SOrElse (S n r a) (S n r a) (a -> STMAction n r)
| forall a. SNew String a (TVar r a -> STMAction n r)
| forall e. Exception e => SThrow e
| SRetry
@ -94,8 +112,6 @@ newtype TVar r a = TVar (TVarId, r a)
-- | The result of an STM transaction, along with which 'TVar's it
-- touched whilst executing.
--
-- @since 0.1.0.0
data Result a =
Success [TVarId] [TVarId] a
-- ^ The transaction completed successfully, reading the first list
@ -108,41 +124,35 @@ data Result a =
-- ^ The transaction aborted by throwing an exception.
deriving Show
-- | This only reduces a 'SomeException' to WHNF.
--
-- @since 0.5.1.0
instance NFData a => NFData (Result a) where
rnf (Success tr1 tr2 a) = rnf (tr1, tr2, a)
rnf (Retry tr) = rnf tr
rnf (Exception e) = e `seq` ()
-- | Check if a 'Result' is a @Success@.
isSTMSuccess :: Result a -> Bool
isSTMSuccess (Success _ _ _) = True
isSTMSuccess _ = False
instance Functor Result where
fmap f (Success rs ws a) = Success rs ws $ f a
fmap _ (Retry rs) = Retry rs
fmap _ (Exception e) = Exception e
instance Foldable Result where
foldMap f (Success _ _ a) = f a
foldMap _ _ = mempty
--------------------------------------------------------------------------------
-- * Execution
-- | Run a transaction, returning the result and new initial 'TVarId'.
-- If the transaction failed, any effects are undone.
runTransaction :: MonadRef r n
=> S n r a
-> IdSource
-> n (Result a, IdSource, [TAction])
runTransaction ma tvid = do
(res, _, tvid', trace) <- doTransaction ma tvid
pure (res, tvid', trace)
-- | Run a STM transaction, returning an action to undo its effects.
doTransaction :: MonadRef r n => M n r a -> IdSource -> n (Result a, n (), IdSource, [TAction])
--
-- If the transaction fails, its effects will automatically be undone,
-- so the undo action returned will be @pure ()@.
doTransaction :: MonadRef r n
=> S n r a
-> IdSource
-> n (Result a, n (), IdSource, [TAction])
doTransaction ma idsource = do
(c, ref) <- runRefCont SStop (Just . Right) (runCont ma)
(c, ref) <- runRefCont SStop (Just . Right) (runSTM ma)
(idsource', undo, readen, written, trace) <- go ref c (pure ()) idsource [] [] []
res <- readRef ref
case res of
Just (Right val) -> pure (Success (nub readen) (nub written) val, undo, idsource', reverse trace)
Just (Left exc) -> undo >> pure (Exception exc, pure (), idsource, reverse trace)
Nothing -> undo >> pure (Retry $ nub readen, pure (), idsource, reverse trace)
@ -168,7 +178,10 @@ doTransaction ma idsource = do
_ -> go ref newAct newUndo newIDSource newReaden newWritten newSofar
-- | Run a transaction for one step.
stepTrans :: MonadRef r n => STMAction n r -> IdSource -> n (STMAction n r, n (), IdSource, [TVarId], [TVarId], TAction)
stepTrans :: MonadRef r n
=> STMAction n r
-> IdSource
-> n (STMAction n r, n (), IdSource, [TVarId], [TVarId], TAction)
stepTrans act idsource = case act of
SCatch h stm c -> stepCatch h stm c
SRead ref c -> stepRead ref c

View File

@ -1,121 +0,0 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
-- |
-- Module : Test.DejaFu.STM
-- Copyright : (c) 2016 Michael Walker
-- License : MIT
-- Maintainer : Michael Walker <mike@barrucadu.co.uk>
-- Stability : experimental
-- Portability : CPP, GeneralizedNewtypeDeriving, TypeFamilies
--
-- A 'MonadSTM' implementation, which can be run on top of 'IO' or
-- 'ST'.
module Test.DejaFu.STM
( -- * The @STMLike@ Monad
STMLike
, STMST
, STMIO
-- * Executing Transactions
, Result(..)
, TAction(..)
, TVarId
, runTransaction
) where
import Control.Applicative (Alternative(..))
import Control.Monad (MonadPlus(..), unless)
import Control.Monad.Catch (MonadCatch(..), MonadThrow(..))
import Control.Monad.Ref (MonadRef)
import Control.Monad.ST (ST)
import Data.IORef (IORef)
import Data.STRef (STRef)
import qualified Control.Monad.STM.Class as C
import Test.DejaFu.Internal
import Test.DejaFu.STM.Internal
import Test.DejaFu.Types
#if MIN_VERSION_base(4,9,0)
import qualified Control.Monad.Fail as Fail
#endif
-- | @since 0.3.0.0
newtype STMLike n r a = S { runSTM :: M n r a } deriving (Functor, Applicative, Monad)
#if MIN_VERSION_base(4,9,0)
-- | @since 0.9.1.0
instance Fail.MonadFail (STMLike r n) where
fail = S . fail
#endif
-- | Create a new STM continuation.
toSTM :: ((a -> STMAction n r) -> STMAction n r) -> STMLike n r a
toSTM = S . cont
-- | A 'MonadSTM' implementation using @ST@, it encapsulates a single
-- atomic transaction. The environment, that is, the collection of
-- defined 'TVar's is implicit, there is no list of them, they exist
-- purely as references. This makes the types simpler, but means you
-- can't really get an aggregate of them (if you ever wanted to for
-- some reason).
--
-- @since 0.3.0.0
type STMST t = STMLike (ST t) (STRef t)
-- | A 'MonadSTM' implementation using @ST@, it encapsulates a single
-- atomic transaction. The environment, that is, the collection of
-- defined 'TVar's is implicit, there is no list of them, they exist
-- purely as references. This makes the types simpler, but means you
-- can't really get an aggregate of them (if you ever wanted to for
-- some reason).
--
-- @since 0.3.0.0
type STMIO = STMLike IO IORef
instance MonadThrow (STMLike n r) where
throwM = toSTM . const . SThrow
instance MonadCatch (STMLike n r) where
catch (S stm) handler = toSTM (SCatch (runSTM . handler) stm)
-- | @since 0.7.2.0
instance Alternative (STMLike n r) where
S a <|> S b = toSTM (SOrElse a b)
empty = toSTM (const SRetry)
-- | @since 0.7.2.0
instance MonadPlus (STMLike n r)
instance C.MonadSTM (STMLike n r) where
type TVar (STMLike n r) = TVar r
#if MIN_VERSION_concurrency(1,2,0)
-- retry and orElse are top-level definitions in
-- Control.Monad.STM.Class in 1.2 and up
#else
retry = empty
orElse = (<|>)
#endif
newTVarN n = toSTM . SNew n
readTVar = toSTM . SRead
writeTVar tvar a = toSTM (\c -> SWrite tvar a (c ()))
-- | Run a transaction, returning the result and new initial
-- 'TVarId'. If the transaction ended by calling 'retry', any 'TVar'
-- modifications are undone.
--
-- @since 0.4.0.0
runTransaction :: MonadRef r n
=> STMLike n r a -> IdSource -> n (Result a, IdSource, [TAction])
runTransaction ma tvid = do
(res, undo, tvid', trace) <- doTransaction (runSTM ma) tvid
unless (isSTMSuccess res) undo
pure (res, tvid', trace)

View File

@ -41,7 +41,6 @@ library
, Test.DejaFu.Defaults
, Test.DejaFu.Refinement
, Test.DejaFu.SCT
, Test.DejaFu.STM
, Test.DejaFu.Schedule
, Test.DejaFu.Types
, Test.DejaFu.Utils
@ -49,10 +48,10 @@ library
, Test.DejaFu.Conc.Internal
, Test.DejaFu.Conc.Internal.Common
, Test.DejaFu.Conc.Internal.Memory
, Test.DejaFu.Conc.Internal.STM
, Test.DejaFu.Conc.Internal.Threading
, Test.DejaFu.Internal
, Test.DejaFu.SCT.Internal
, Test.DejaFu.STM.Internal
-- other-modules:
-- other-extensions: