2015-01-21 18:31:10 +03:00
|
|
|
{-# LANGUAGE Rank2Types #-}
|
2015-01-09 05:35:28 +03:00
|
|
|
|
|
|
|
-- | A runner for concurrent monads to systematically detect
|
|
|
|
-- concurrency errors such as data races and deadlocks: internal definitions.
|
2015-01-31 18:50:54 +03:00
|
|
|
module Test.DejaFu.SCT.Internal where
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-01-24 13:57:05 +03:00
|
|
|
import Control.Monad.Loops (unfoldrM)
|
2015-01-12 19:28:04 +03:00
|
|
|
import Data.List (unfoldr)
|
2015-01-31 18:50:54 +03:00
|
|
|
import Test.DejaFu.Deterministic
|
2015-02-01 04:21:42 +03:00
|
|
|
import Test.DejaFu.Deterministic.IO (ConcIO, runConcIO)
|
2015-01-19 14:50:43 +03:00
|
|
|
|
2015-01-09 05:35:28 +03:00
|
|
|
-- * SCT Runners
|
|
|
|
|
|
|
|
-- | Run a concurrent program under a given scheduler a number of
|
2015-02-01 04:21:42 +03:00
|
|
|
-- times, collecting the results and the trace that gave rise to them.
|
2015-01-09 05:35:28 +03:00
|
|
|
--
|
2015-02-01 04:21:42 +03:00
|
|
|
-- The initial state for each run is the final state of the prior run,
|
2015-01-09 05:35:28 +03:00
|
|
|
-- so it is important that the scheduler actually maintain some
|
|
|
|
-- internal state, or all the results will be identical.
|
2015-02-01 04:21:42 +03:00
|
|
|
runSCT :: Scheduler s -- ^ The scheduler
|
|
|
|
-> s -- ^ The scheduler's initial state
|
|
|
|
-> Int -- ^ The number of executions to perform
|
|
|
|
-> (forall t. Conc t a) -- ^ The computation to test
|
2015-02-06 18:24:47 +03:00
|
|
|
-> [(Either Failure a, Trace)]
|
2015-01-12 19:04:23 +03:00
|
|
|
runSCT sched s n = runSCT' sched (s, n) term step where
|
|
|
|
term (_, g) = g == 0
|
|
|
|
step (s', g) _ = (s', g - 1)
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-02-01 04:21:42 +03:00
|
|
|
-- | Variant of 'runSCT' for computations which do 'IO'.
|
2015-02-06 18:24:47 +03:00
|
|
|
runSCTIO :: Scheduler s -> s -> Int -> (forall t. ConcIO t a) -> IO [(Either Failure a, Trace)]
|
2015-01-12 19:04:23 +03:00
|
|
|
runSCTIO sched s n = runSCTIO' sched (s, n) term step where
|
|
|
|
term (_, g) = g == 0
|
|
|
|
step (s', g) _ = (s', g - 1)
|
2015-01-09 05:35:28 +03:00
|
|
|
|
|
|
|
-- | Run a concurrent program under a given scheduler, where the SCT
|
|
|
|
-- runner itself maintains some internal state, and has a function to
|
|
|
|
-- produce a new scheduler state for each run, and decide termination
|
|
|
|
-- based on the internal state.
|
|
|
|
--
|
|
|
|
-- Note: the state step function takes the state returned by the
|
|
|
|
-- scheduler, not the initial state!
|
2015-02-01 04:21:42 +03:00
|
|
|
runSCT' :: Scheduler s -- ^ The scheduler
|
2015-01-12 19:04:23 +03:00
|
|
|
-> (s, g) -- ^ The scheduler's and runner's initial states
|
|
|
|
-> ((s, g) -> Bool) -- ^ Termination decider
|
2015-02-01 04:21:42 +03:00
|
|
|
-> ((s, g) -> Trace -> (s, g)) -- ^ State step function
|
|
|
|
-> (forall t. Conc t a) -- ^ The computation to test
|
2015-02-06 18:24:47 +03:00
|
|
|
-> [(Either Failure a, Trace)]
|
2015-01-12 19:28:04 +03:00
|
|
|
runSCT' sched initial term step c = unfoldr go initial where
|
|
|
|
go sg@(s, g)
|
|
|
|
| term sg = Nothing
|
2015-01-19 14:06:40 +03:00
|
|
|
| otherwise = res `seq` Just ((res, trace), sg')
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-01-12 19:28:04 +03:00
|
|
|
where
|
2015-02-01 04:21:42 +03:00
|
|
|
(res, s', trace) = runConc sched s c
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-01-12 19:28:04 +03:00
|
|
|
sg' = step (s', g) trace
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-02-01 04:21:42 +03:00
|
|
|
-- | Variant of 'runSCT'' for computations which do 'IO'.
|
2015-02-06 18:24:47 +03:00
|
|
|
runSCTIO' :: Scheduler s -> (s, g) -> ((s, g) -> Bool) -> ((s, g) -> Trace -> (s, g)) -> (forall t. ConcIO t a) -> IO [(Either Failure a, Trace)]
|
2015-01-12 19:28:04 +03:00
|
|
|
runSCTIO' sched initial term step c = unfoldrM go initial where
|
|
|
|
go sg@(s, g)
|
|
|
|
| term sg = return Nothing
|
|
|
|
| otherwise = do
|
2015-02-01 04:21:42 +03:00
|
|
|
(res, s', trace) <- runConcIO sched s c
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-01-12 19:28:04 +03:00
|
|
|
let sg' = step (s', g) trace
|
2015-01-09 05:35:28 +03:00
|
|
|
|
2015-01-19 14:06:40 +03:00
|
|
|
res `seq` return (Just ((res, trace), sg'))
|