11{-# LANGUAGE NamedFieldPuns #-}
2+ {-# LANGUAGE ImpredicativeTypes #-}
23
34-- | Define several variant models of /counters/ which are useful to
45-- test or use examples for various behaviours of the runtime.
56module Spec.DynamicLogic.Counters where
67
7- import Control.Concurrent
88import Control.Monad.Reader
9- import Data.IORef
10- import Test.QuickCheck ( frequency , Property )
9+ import Test.QuickCheck hiding ( Some )
10+ import Test.QuickCheck.Gen.Unsafe
1111import Test.QuickCheck.StateModel
1212import Test.QuickCheck.ParallelActions
1313import Test.QuickCheck.Extras
1414import Test.QuickCheck.Monadic
1515
16+ import Control.Concurrent.Class.MonadSTM
17+ import Control.Monad.Class.MonadFork
18+ import Control.Monad.Class.MonadThrow
19+ import Control.Monad.Class.MonadTimer
20+ import Control.Monad.Class.MonadTest
21+ import Control.Monad.IOSim
22+
1623-- A very simple model with a single action that always succeed in
1724-- predictable way. This model is useful for testing the runtime.
1825newtype SimpleCounter = SimpleCounter { count :: Int }
@@ -33,10 +40,13 @@ instance StateModel SimpleCounter where
3340
3441 nextState SimpleCounter {count} IncSimple _ = SimpleCounter (count + 1 )
3542
36- instance RunModel SimpleCounter (ReaderT (IORef Int ) IO ) where
43+ instance RunModel SimpleCounter (ReaderT (CounterState IO ) IO ) where
3744 perform _ IncSimple _ = do
38- ref <- ask
39- lift $ atomicModifyIORef' ref (\ count -> (succ count, count))
45+ ref <- asks counterState
46+ lift $ atomically $ do
47+ c <- readTVar ref
48+ writeTVar ref (c + 1 )
49+ return c
4050
4151-- A very simple model with a single action whose postcondition fails in a
4252-- predictable way. This model is useful for testing the runtime.
@@ -58,10 +68,13 @@ instance StateModel FailingCounter where
5868
5969 nextState FailingCounter {failingCount} Inc' _ = FailingCounter (failingCount + 1 )
6070
61- instance RunModel FailingCounter (ReaderT (IORef Int ) IO ) where
71+ instance RunModel FailingCounter (ReaderT (CounterState IO ) IO ) where
6272 perform _ Inc' _ = do
63- ref <- ask
64- lift $ atomicModifyIORef' ref (\ count -> (succ count, count))
73+ ref <- asks counterState
74+ lift $ atomically $ do
75+ c <- readTVar ref
76+ writeTVar ref (c + 1 )
77+ return c
6578
6679 postcondition (_, FailingCounter {failingCount}) _ _ _ = pure $ failingCount < 4
6780
@@ -86,37 +99,74 @@ instance StateModel Counter where
8699 nextState (Counter n) Inc _ = Counter (n + 1 )
87100 nextState _ Reset _ = Counter 0
88101
89- instance RunModel Counter (ReaderT (IORef Int ) IO ) where
102+ newtype CounterState m = CounterState { counterState :: TVar m Int }
103+
104+ setupCounterState :: MonadSTM m => m (CounterState m )
105+ setupCounterState = CounterState <$> atomically (newTVar 0 )
106+
107+ instance (MonadSTM m ) => RunModel Counter (ReaderT (CounterState m ) m ) where
90108 perform _ Inc _ = do
91- ref <- ask
109+ ref <- asks counterState
92110 lift $ do
93- n <- readIORef ref
94- threadDelay 1000
95- writeIORef ref (n + 1 )
111+ n <- atomically $ readTVar ref
112+ atomically $ writeTVar ref (n + 1 )
96113 perform _ Reset _ = do
97- ref <- ask
114+ ref <- asks counterState
98115 lift $ do
99- n <- readIORef ref
100- threadDelay 1000
101- writeIORef ref 0
116+ n <- atomically $ readTVar ref
117+ atomically $ writeTVar ref 0
102118 pure n
103119
104120 postcondition (Counter n, _) Reset _ res = pure $ n == res
105121 postcondition _ _ _ _ = pure True
106122
107- instance RunModelPar Counter (ReaderT (IORef Int ) IO ) where
123+ instance MonadSTM m => RunModelPar Counter (ReaderT (CounterState m ) m ) where
108124 performPar Inc _ = do
109- ref <- ask
110- -- lift $ atomicModifyIORef ref (\ c -> (c+1, ()))
125+ ref <- asks counterState
126+ -- lift $ atomically $ modifyTVar ref (\ c -> (c+1, ()))
111127 lift $ do
112- n <- readIORef ref
113- threadDelay 5000
114- writeIORef ref (n + 1 )
128+ n <- atomically $ readTVar ref
129+ atomically $ writeTVar ref (n + 1 )
115130 performPar Reset _ = do
116- ref <- ask
117- lift $ atomicModifyIORef ref (\ c -> (0 , c))
131+ ref <- asks counterState
132+ lift $ atomically $ do
133+ c <- readTVar ref
134+ writeTVar ref 0
135+ return c
136+
137+ prop_counter :: Actions Counter -> Property
138+ prop_counter as = monadicIO $ do
139+ ref <- lift $ atomically $ newTVar (0 :: Int )
140+ runPropertyReaderT (runActions as) (CounterState ref :: CounterState IO )
141+ assert True
118142
119143prop_counter_par :: ParallelActions Counter -> Property
120144prop_counter_par as = always 10 $ monadicIO $ do
121- ref <- lift $ newIORef ( 0 :: Int )
145+ ref <- lift setupCounterState
122146 runPropertyReaderT (runParActions as) ref
147+
148+ prop_counter_parIOSimPor :: ParallelActions Counter -> Property
149+ prop_counter_parIOSimPor as =
150+ monadicIOSimPOR_ prop
151+ where
152+ prop :: forall s . PropertyM (IOSim s ) ()
153+ prop = do
154+ ref <- lift $ atomically $ newTVar (0 :: Int )
155+ lift $ exploreRaces
156+ runPropertyReaderT (runParActions as) (CounterState ref :: CounterState (IOSim s ))
157+
158+ monadicIOSimPOR_ :: Testable a => (forall s . PropertyM (IOSim s ) a ) -> Property
159+ monadicIOSimPOR_ prop = forAllBlind prop' $ \ p -> exploreSimTrace id p $ \ _ tr ->
160+ either (flip counterexample False . show ) id $ traceResult False tr
161+ where
162+ prop' :: Gen (forall s . IOSim s Property )
163+ prop' = do
164+ Capture eval <- capture
165+ pure $ eval $ monadic' prop
166+
167+ instance Forking (IOSim s ) where
168+ forkThread io = do
169+ t <- atomically newEmptyTMVar
170+ forkIO $ io >>= atomically . putTMVar t
171+ return $ atomically $ takeTMVar t
172+
0 commit comments