packages feed

dejafu 1.3.0.1 → 1.3.0.2

raw patch · 9 files changed

+101/−523 lines, 9 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Test.DejaFu.Conc.Internal.Memory: commitThreadId :: ThreadId -> Maybe CRefId -> ThreadId
- Test.DejaFu.Internal: [_equality] :: Settings n a -> Maybe (a -> a -> Bool)
- Test.DejaFu.Internal: [_simplify] :: Settings n a -> Bool
- Test.DejaFu.SCT.Internal: dropCommits :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: fromId :: Coercible a Id => a -> Int
- Test.DejaFu.SCT.Internal: lexicoNormalForm :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: permuteBy :: MemType -> [ThreadId -> ThreadId -> Bool] -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: pullBack :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: pushForward :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: renumber :: MemType -> Int -> Int -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: replay :: (MonadConc n, MonadRef r n) => (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace)) -> [(ThreadId, ThreadAction)] -> n (Either Failure a, [(ThreadId, ThreadAction)], Trace)
- Test.DejaFu.SCT.Internal: sct :: (MonadConc n, MonadRef r n) => Settings n a -> ([ThreadId] -> s) -> (s -> Maybe t) -> ((Scheduler g -> g -> n (Either Failure a, g, Trace)) -> s -> t -> n (s, Maybe (Either Failure a, Trace))) -> ConcT r n a -> n [(Either Failure a, Trace)]
- Test.DejaFu.SCT.Internal: sct' :: (MonadConc n, MonadRef r n) => Settings n a -> s -> (s -> Maybe t) -> (s -> t -> n (s, Maybe (Either Failure a, Trace))) -> (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace)) -> n [(Either Failure a, Trace)]
- Test.DejaFu.SCT.Internal: simplify :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]
- Test.DejaFu.SCT.Internal: simplifyExecution :: (MonadConc n, MonadRef r n) => Settings n a -> (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace)) -> Either Failure a -> Trace -> n (Either Failure a, Trace)
- Test.DejaFu.SCT.Internal: toId :: Coercible Id a => Int -> a
- Test.DejaFu.Settings: lequality :: Lens' (Settings n a) (Maybe (a -> a -> Bool))
- Test.DejaFu.Settings: lsimplify :: Lens' (Settings n a) Bool
- Test.DejaFu.Utils: toTIdTrace :: Trace -> [(ThreadId, ThreadAction)]
- Test.DejaFu.Internal: Settings :: Way -> MemType -> Maybe (Either Failure a -> Maybe Discard) -> Maybe (a -> String) -> Maybe (String -> n ()) -> Maybe (Either Failure a -> Bool) -> Maybe (a -> a -> Bool) -> Bool -> Settings n a
+ Test.DejaFu.Internal: Settings :: Way -> MemType -> Maybe (Either Failure a -> Maybe Discard) -> Maybe (a -> String) -> Maybe (String -> n ()) -> Maybe (Either Failure a -> Bool) -> Settings n a
- Test.DejaFu.SCT.Internal.DPOR: dporSched :: MemType -> IncrementalBoundFunc k -> Scheduler (DPORSchedState k)
+ Test.DejaFu.SCT.Internal.DPOR: dporSched :: IncrementalBoundFunc k -> Scheduler (DPORSchedState k)
- Test.DejaFu.SCT.Internal.DPOR: findBacktrackSteps :: MemType -> BacktrackFunc -> Bool -> Seq ([(ThreadId, Lookahead)], [ThreadId]) -> Trace -> [BacktrackStep]
+ Test.DejaFu.SCT.Internal.DPOR: findBacktrackSteps :: BacktrackFunc -> Bool -> Seq ([(ThreadId, Lookahead)], [ThreadId]) -> Trace -> [BacktrackStep]
- Test.DejaFu.SCT.Internal.DPOR: incorporateTrace :: MemType -> Bool -> Trace -> DPOR -> DPOR
+ Test.DejaFu.SCT.Internal.DPOR: incorporateTrace :: Bool -> Trace -> DPOR -> DPOR
- Test.DejaFu.SCT.Internal.DPOR: updateCRState :: MemType -> ThreadAction -> Map CRefId Bool -> Map CRefId Bool
+ Test.DejaFu.SCT.Internal.DPOR: updateCRState :: ThreadAction -> Map CRefId Bool -> Map CRefId Bool
- Test.DejaFu.SCT.Internal.DPOR: updateDepState :: MemType -> DepState -> ThreadId -> ThreadAction -> DepState
+ Test.DejaFu.SCT.Internal.DPOR: updateDepState :: DepState -> ThreadId -> ThreadAction -> DepState

Files

CHANGELOG.rst view
@@ -6,25 +6,17 @@  .. _PVP: https://pvp.haskell.org/ -unreleased-------------Added-~~~~~--* (:issue:`183`) SCT settings for trace simplification:--    * ``Test.DejaFu.Settings.lequality``-    * ``Test.DejaFu.Settings.lsimplify``+1.3.0.2 (2018-03-11)+-------------------- -* (:issue:`183`) ``Test.DejaFu.Utils.toTIdTrace`` to extract thread-  IDs from a trace.+* Git: :tag:`dejafu-1.3.0.2`+* Hackage: :hackage:`dejafu-1.3.0.2` -Performance-~~~~~~~~~~~+Fixed+~~~~~ -* (:issue:`183`) Prune some unnecessary interleavings of ``CRef``-  actions in systematic testing when using sequential consistency.+* (:pull:`244`) Add missing dependency for ``setNumCapabilities``+  actions.   1.3.0.1 (2018-03-08)
Test/DejaFu/Conc/Internal/Memory.hs view
@@ -131,18 +131,15 @@ -- | Add phantom threads to the thread list to commit pending writes. addCommitThreads :: WriteBuffer r -> Threads n r -> Threads n r addCommitThreads (WriteBuffer wb) ts = ts <> M.fromList phantoms where-  phantoms = [ (uncurry commitThreadId k, mkthread c)+  phantoms = [ (ThreadId (Id Nothing . negate $ commitTidOf k), mkthread c)              | (k, b) <- M.toList wb              , c <- maybeToList (go $ viewl b)              ]   go (BufferedWrite tid (CRef crid _) _ :< _) = Just $ ACommit tid crid   go EmptyL = Nothing --- | The ID of a commit thread.-commitThreadId :: ThreadId -> Maybe CRefId -> ThreadId-commitThreadId (ThreadId (Id _ t)) = ThreadId . Id Nothing . negate . go where-  go (Just (CRefId (Id _ c))) = t + 1 + c * 10000-  go Nothing = t + 1+  commitTidOf (ThreadId (Id _ t), Nothing) = t + 1+  commitTidOf (ThreadId (Id _ t), Just (CRefId (Id _ c))) = t + 1 + c * 10000  -- | Remove phantom threads. delCommitThreads :: Threads n r -> Threads n r
Test/DejaFu/Internal.hs view
@@ -37,8 +37,6 @@   , _debugShow :: Maybe (a -> String)   , _debugPrint :: Maybe (String -> n ())   , _earlyExit :: Maybe (Either Failure a -> Bool)-  , _equality :: Maybe (a -> a -> Bool)-  , _simplify  :: Bool   }  -- | How to explore the possible executions of a concurrent program.
Test/DejaFu/SCT.hs view
@@ -1,10 +1,13 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE LambdaCase #-}+ -- | -- Module      : Test.DejaFu.SCT -- Copyright   : (c) 2015--2018 Michael Walker -- License     : MIT -- Maintainer  : Michael Walker <mike@barrucadu.co.uk> -- Stability   : experimental--- Portability : portable+-- Portability : BangPatterns, LambdaCase -- -- Systematic testing for concurrent computations. module Test.DejaFu.SCT@@ -47,7 +50,6 @@  import           Test.DejaFu.Conc import           Test.DejaFu.Internal-import           Test.DejaFu.SCT.Internal import           Test.DejaFu.SCT.Internal.DPOR import           Test.DejaFu.SCT.Internal.Weighted import           Test.DejaFu.Settings@@ -197,13 +199,13 @@          check = findSchedulePrefix -        step run dp (prefix, conservative, sleep) = do+        step dp (prefix, conservative, sleep) run = do           (res, s, trace) <- run-            (dporSched (_memtype settings) (cBound cb0))+            (dporSched (cBound cb0))             (initialDPORSchedState sleep prefix) -          let bpoints = findBacktrackSteps (_memtype settings) (cBacktrack cb0) (schedBoundKill s) (schedBPoints s) trace-          let newDPOR = incorporateTrace (_memtype settings) conservative trace dp+          let bpoints = findBacktrackSteps (cBacktrack cb0) (schedBoundKill s) (schedBPoints s) trace+          let newDPOR = incorporateTrace conservative trace dp            pure $ if schedIgnore s                  then (force newDPOR, Nothing)@@ -216,7 +218,7 @@         check (_, 0) = Nothing         check s = Just s -        step run _ (g, n) = do+        step _ (g, n) run = do           (res, s, trace) <- run             (randSched $ \g' -> (1, g'))             (initialRandSchedState Nothing g)@@ -229,8 +231,8 @@         check (_, 0, _, _) = Nothing         check s = Just s -        step run s (g, n, 0, _) = step run s (g, n, max 1 use0, M.empty)-        step run _ (g, n, use, ws) = do+        step s (g, n, 0, _) run = step s (g, n, max 1 use0, M.empty) run+        step _ (g, n, use, ws) run = do           (res, s, trace) <- run             (randSched $ randomR (1, 50))             (initialRandSchedState (Just ws) g)@@ -504,6 +506,45 @@ sctWeightedRandomDiscard discard memtype g lim use = runSCTWithSettings $   set ldiscard (Just discard) (fromWayAndMemType (swarmy g lim use) memtype) {-# DEPRECATED sctWeightedRandomDiscard "Use runSCTWithSettings instead" #-}++-- | General-purpose SCT function.+sct :: (MonadConc n, MonadRef r n)+  => Settings n a+  -- ^ The SCT settings ('Way' is ignored)+  -> ([ThreadId] -> s)+  -- ^ Initial state+  -> (s -> Maybe t)+  -- ^ State predicate+  -> (s -> t -> (Scheduler g -> g -> n (Either Failure a, g, Trace)) -> n (s, Maybe (Either Failure a, Trace)))+  -- ^ Run the computation and update the state+  -> ConcT r n a+  -> n [(Either Failure a, Trace)]+sct settings s0 sfun srun conc+    | canDCSnapshot conc = runForDCSnapshot conc >>= \case+        Just (Right snap, _) -> go (runSnap snap) (fst (threadsFromDCSnapshot snap))+        Just (Left f, trace) -> pure [(Left f, trace)]+        _ -> do+          debugPrint "Failed to construct snapshot, continuing without."+          go runFull [initialThread]+    | otherwise = go runFull [initialThread]+  where+    go run = go' Nothing . s0 where+      go' (Just res) _ | earlyExit res = pure []+      go' _ !s = case sfun s of+        Just t -> srun s t run >>= \case+          (s', Just (res, trace)) -> case discard res of+            Just DiscardResultAndTrace -> go' (Just res) s'+            Just DiscardTrace -> ((res, []):) <$> go' (Just res) s'+            Nothing -> ((res, trace):) <$> go' (Just res) s'+          (s', Nothing) -> go' Nothing s'+        Nothing -> pure []++    runFull sched s = runConcurrent sched (_memtype settings) s conc+    runSnap snap sched s = runWithDCSnapshot sched (_memtype settings) s snap++    debugPrint = fromMaybe (const (pure ())) (_debugPrint settings)+    earlyExit = fromMaybe (const False) (_earlyExit settings)+    discard = fromMaybe (const Nothing) (_discard settings)  ------------------------------------------------------------------------------- -- Utilities
− Test/DejaFu/SCT/Internal.hs
@@ -1,359 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE RankNTypes #-}---- |--- Module      : Test.DejaFu.SCT.Internal--- Copyright   : (c) 2018 Michael Walker--- License     : MIT--- Maintainer  : Michael Walker <mike@barrucadu.co.uk>--- Stability   : experimental--- Portability : BangPatterns, FlexibleContexts, LambdaCase, RankNTypes------ Internal types and functions for SCT.  This module is NOT--- considered to form part of the public interface of this library.-module Test.DejaFu.SCT.Internal where--import           Control.Monad.Conc.Class         (MonadConc)-import           Control.Monad.Ref                (MonadRef)-import           Data.Coerce                      (Coercible, coerce)-import qualified Data.IntMap.Strict               as I-import           Data.List                        (find, mapAccumL)-import           Data.Maybe                       (fromMaybe)--import           Test.DejaFu.Conc-import           Test.DejaFu.Conc.Internal.Memory (commitThreadId)-import           Test.DejaFu.Internal-import           Test.DejaFu.Schedule             (Scheduler(..))-import           Test.DejaFu.SCT.Internal.DPOR-import           Test.DejaFu.Types-import           Test.DejaFu.Utils------------------------------------------------------------------------------------ * Exploration---- | General-purpose SCT function.-sct :: (MonadConc n, MonadRef r n)-  => Settings n a-  -- ^ The SCT settings ('Way' is ignored)-  -> ([ThreadId] -> s)-  -- ^ Initial state-  -> (s -> Maybe t)-  -- ^ State predicate-  -> ((Scheduler g -> g -> n (Either Failure a, g, Trace)) -> s -> t -> n (s, Maybe (Either Failure a, Trace)))-  -- ^ Run the computation and update the state-  -> ConcT r n a-  -> n [(Either Failure a, Trace)]-sct settings s0 sfun srun conc-    | canDCSnapshot conc = runForDCSnapshot conc >>= \case-        Just (Right snap, _) -> sct' settings (s0 (fst (threadsFromDCSnapshot snap))) sfun (srun (runSnap snap)) (runSnap snap)-        Just (Left f, trace) -> pure [(Left f, trace)]-        _ -> do-          debugPrint "Failed to construct snapshot, continuing without."-          sct' settings (s0 [initialThread]) sfun (srun runFull) runFull-    | otherwise = sct' settings (s0 [initialThread]) sfun (srun runFull) runFull-  where-    runFull sched s = runConcurrent sched (_memtype settings) s conc-    runSnap snap sched s = runWithDCSnapshot sched (_memtype settings) s snap--    debugPrint = fromMaybe (const (pure ())) (_debugPrint settings)---- | Like 'sct' but given a function to run the computation.-sct' :: (MonadConc n, MonadRef r n)-  => Settings n a-  -- ^ The SCT settings ('Way' is ignored)-  -> s-  -- ^ Initial state-  -> (s -> Maybe t)-  -- ^ State predicate-  -> (s -> t -> n (s, Maybe (Either Failure a, Trace)))-  -- ^ Run the computation and update the state-  -> (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace))-  -- ^ Just run the computation-  -> n [(Either Failure a, Trace)]-sct' settings s0 sfun srun run = go Nothing [] s0 where-  go (Just res) _ _ | earlyExit res = pure []-  go _ seen !s = case sfun s of-    Just t -> srun s t >>= \case-      (s', Just (res, trace)) -> case discard res of-        Just DiscardResultAndTrace -> go (Just res) seen s'-        Just DiscardTrace -> result res [] seen s'-        Nothing -> result res trace seen s'-      (s', Nothing) -> go Nothing seen s'-    Nothing -> pure []--  -- Sadly, we have to use a list to store the set of unique results,-  -- as we don't have an @Ord a@ dict hanging around.  I suspect that-  -- most test cases will have a relatively small number of unique-  -- results, compared to the number of executions, however.-  -- Pathological cases (like IORef ones in dejafu-tests which produce-  -- a different result on every execution) are probably uncommon.-  result = case _equality settings of-    Just f -> \res trace seen s ->-      let eq cmp (Right a1) (Right a2) = cmp a1 a2-          eq _   (Left  e1) (Left  e2) = e1 == e2-          eq _ _ _ = False-      in if any (eq f res) seen-         then go (Just res) seen s-         else dosimplify res trace (res:seen) s-    Nothing -> dosimplify--  dosimplify res [] seen s = ((res, []) :) <$> go (Just res) seen s-  dosimplify res trace seen s-    | not (_simplify settings) = ((res, trace) :) <$> go (Just res) seen s-    | otherwise = do-        shrunk <- simplifyExecution settings run res trace-        (shrunk :) <$> go (Just res) seen s--  earlyExit = fromMaybe (const False) (_earlyExit settings)-  discard = fromMaybe (const Nothing) (_discard settings)---- | Given a result and a trace, produce a more minimal trace.------ In principle, simplification is semantics preserving and can be--- done without needing to execute the computation again.  However,--- there are two good reasons to do so:------  * It's a sanity check that there are no bugs.---  * It's easier to generate a reduced sequence of scheduling---    decisions and let dejafu generate the full trace, than to---    generate a reduced trace directly------ Unlike shrinking in randomised property-testing tools like--- QuickCheck or Hedgehog, we only run the test case /once/, at the--- end, rather than after every simplification step.-simplifyExecution :: (MonadConc n, MonadRef r n)-  => Settings n a-  -- ^ The SCT settings ('Way' is ignored)-  -> (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace))-  -- ^ Just run the computation-  -> Either Failure a-  -- ^ The expected result-  -> Trace-  -> n (Either Failure a, Trace)-simplifyExecution settings run res trace-    | tidTrace == simplifiedTrace = do-        debugPrint ("Simplifying new result '" ++ p res ++ "': no simplification possible!")-        pure (res, trace)-    | otherwise = do-        debugPrint ("Simplifying new result '" ++ p res ++ "': OK!")-        (res', _, trace') <- replay run simplifiedTrace-        case (_equality settings, res, res') of-          (Just f,  Right a1, Right a2) | f a1 a2  -> pure (res', trace')-          (_,       Left  e1, Left  e2) | e1 == e2 -> pure (res', trace')-          (Nothing, Right _,  Right _) -> pure (res', trace') -- this is a risky case!-          _ -> do-            debugPrint ("Got a different result after simplifying: '" ++ p res ++ "' /= '" ++ p res' ++ "'")-            pure (res, trace)-  where-    tidTrace = toTIdTrace trace-    simplifiedTrace = simplify (_memtype settings) tidTrace--    debugPrint = fromMaybe (const (pure ())) (_debugPrint settings)-    debugShow = fromMaybe (const "_") (_debugShow settings)-    p = either show debugShow---- | Replay an execution.-replay :: (MonadConc n, MonadRef r n)-  => (forall x. Scheduler x -> x -> n (Either Failure a, x, Trace))-  -- ^ Run the computation-  -> [(ThreadId, ThreadAction)]-  -- ^ The reduced sequence of scheduling decisions-  -> n (Either Failure a, [(ThreadId, ThreadAction)], Trace)-replay run = run (Scheduler (const sched)) where-    sched runnable ((t, Stop):ts) = case findThread t runnable of-      Just t' -> (Just t', ts)-      Nothing -> sched runnable ts-    sched runnable ((t, _):ts) = (findThread t runnable, ts)-    sched _ _ = (Nothing, [])--    -- find a thread ignoring names-    findThread tid0 =-      fmap fst . find (\(tid,_) -> fromId tid == fromId tid0)------------------------------------------------------------------------------------ * Schedule simplification---- | Simplify a trace by permuting adjacent independent actions to--- reduce context switching.-simplify :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]-simplify memtype trc0 = loop (length trc0) (prepare trc0) where-  prepare = dropCommits memtype . lexicoNormalForm memtype-  step = pushForward memtype . pullBack memtype--  loop 0 trc = trc-  loop n trc =-    let trc' = step trc-    in if trc' /= trc then loop (n-1) trc' else trc---- | Put a trace into lexicographic (by thread ID) normal form.-lexicoNormalForm :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]-lexicoNormalForm memtype = go where-  go trc =-    let trc' = permuteBy memtype (repeat (>)) trc-    in if trc == trc' then trc else go trc'---- | Swap adjacent independent actions in the trace if a predicate--- holds.-permuteBy-  :: MemType-  -> [ThreadId -> ThreadId -> Bool]-  -> [(ThreadId, ThreadAction)]-  -> [(ThreadId, ThreadAction)]-permuteBy memtype = go initialDepState where-  go ds (p:ps) (t1@(tid1, ta1):t2@(tid2, ta2):trc)-    | independent ds tid1 ta1 tid2 ta2 && p tid1 tid2 = go' ds ps t2 (t1 : trc)-    | otherwise = go' ds ps t1 (t2 : trc)-  go _ _ trc = trc--  go' ds ps t@(tid, ta) trc = t : go (updateDepState memtype ds tid ta) ps trc---- | Throw away commit actions which are followed by a memory barrier.-dropCommits :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]-dropCommits SequentialConsistency = id-dropCommits memtype = go initialDepState where-  go ds (t1@(tid1, ta1@(CommitCRef _ _)):t2@(tid2, ta2):trc)-    | isBarrier (simplifyAction ta2) = go ds (t2:trc)-    | independent ds tid1 ta1 tid2 ta2 = t2 : go (updateDepState memtype ds tid2 ta2) (t1:trc)-  go ds (t@(tid,ta):trc) = t : go (updateDepState memtype ds tid ta) trc-  go _ [] = []---- | Attempt to reduce context switches by \"pulling\" thread actions--- back to a prior execution of that thread.------ Simple example, say we have @[(tidA, act1), (tidB, act2), (tidA,--- act3)]@, where @act2@ and @act3@ are independent.  In this case--- 'pullBack' will swap them, giving the sequence @[(tidA, act1),--- (tidA, act3), (tidB, act2)]@.  It works for arbitrary separations.-pullBack :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]-pullBack memtype = go initialDepState where-  go ds (t1@(tid1, ta1):trc@((tid2, _):_)) =-    let ds' = updateDepState memtype ds tid1 ta1-        trc' = if tid1 /= tid2-               then maybe trc (uncurry (:)) (findAction tid1 ds' trc)-               else trc-    in t1 : go ds' trc'-  go _ trc = trc--  findAction tid0 = fgo where-    fgo ds (t@(tid, ta):trc)-      | tid == tid0 = Just (t, trc)-      | otherwise = case fgo (updateDepState memtype ds tid ta) trc of-          Just (ft@(ftid, fa), trc')-            | independent ds tid ta ftid fa -> Just (ft, t:trc')-          _ -> Nothing-    fgo _ _ = Nothing---- | Attempt to reduce context switches by \"pushing\" thread actions--- forward to a future execution of that thread.------ This is kind of the opposite of 'pullBack', but there are cases--- where one applies but not the other.------ Simple example, say we have @[(tidA, act1), (tidB, act2), (tidA,--- act3)]@, where @act1@ and @act2@ are independent.  In this case--- 'pushForward' will swap them, giving the sequence @[(tidB, act2),--- (tidA, act1), (tidA, act3)]@.  It works for arbitrary separations.-pushForward :: MemType -> [(ThreadId, ThreadAction)] -> [(ThreadId, ThreadAction)]-pushForward memtype = go initialDepState where-  go ds (t1@(tid1, ta1):trc@((tid2, _):_)) =-    let ds' = updateDepState memtype ds tid1 ta1-    in if tid1 /= tid2-       then maybe (t1 : go ds' trc) (go ds) (findAction tid1 ta1 ds trc)-       else t1 : go ds' trc-  go _ trc = trc--  findAction tid0 ta0 = fgo where-    fgo ds (t@(tid, ta):trc)-      | tid == tid0 = Just ((tid0, ta0) : t : trc)-      | independent ds tid0 ta0 tid ta = (t:) <$> fgo (updateDepState memtype ds tid ta) trc-      | otherwise = Nothing-    fgo _ _ = Nothing---- | Re-number threads and CRefs.------ Permuting forks or newCRefs makes the existing numbering invalid,--- which then causes problems for scheduling.  Just re-numbering--- threads isn't enough, as CRef IDs are used to determine commit--- thread IDs.------ Renumbered things will not fix their names, so don't rely on those--- at all.-renumber-  :: MemType-  -- ^ The memory model determines how commit threads are numbered.-  -> Int-  -- ^ First free thread ID.-  -> Int-  -- ^ First free @CRef@ ID.-  -> [(ThreadId, ThreadAction)]-  -> [(ThreadId, ThreadAction)]-renumber memtype tid0 crid0 = snd . mapAccumL go (I.empty, tid0, I.empty, crid0) where-  go s@(tidmap, _, cridmap, _) (_, CommitCRef tid crid) =-    let tid'  = renumbered tidmap  tid-        crid' = renumbered cridmap crid-        act' = CommitCRef tid' crid'-    in case memtype of-         PartialStoreOrder -> (s, (commitThreadId tid' (Just crid'), act'))-         _ -> (s, (commitThreadId tid' Nothing, act'))-  go s@(tidmap, _, _, _) (tid, act) =-    let (s', act') = updateAction s act-    in (s', (renumbered tidmap tid, act'))--  -- I can't help but feel there should be some generic programming-  -- solution to this sort of thing (and to the many other functions-  -- operating over @ThreadAction@s / @Lookahead@s)-  updateAction (tidmap, nexttid, cridmap, nextcrid) (Fork old) =-    let tidmap' = I.insert (fromId old) nexttid tidmap-        nexttid' = nexttid + 1-    in ((tidmap', nexttid', cridmap, nextcrid), Fork (toId nexttid))-  updateAction (tidmap, nexttid, cridmap, nextcrid) (ForkOS old) =-    let tidmap' = I.insert (fromId old) nexttid tidmap-        nexttid' = nexttid + 1-    in ((tidmap', nexttid', cridmap, nextcrid), ForkOS (toId nexttid))-  updateAction s@(tidmap, _, _, _) (PutMVar mvid olds) =-    (s, PutMVar mvid (map (renumbered tidmap) olds))-  updateAction s@(tidmap, _, _, _) (TryPutMVar mvid b olds) =-    (s, TryPutMVar mvid b (map (renumbered tidmap) olds))-  updateAction s@(tidmap, _, _, _) (TakeMVar mvid olds) =-    (s, TakeMVar mvid (map (renumbered tidmap) olds))-  updateAction s@(tidmap, _, _, _) (TryTakeMVar mvid b olds) =-    (s, TryTakeMVar mvid b (map (renumbered tidmap) olds))-  updateAction (tidmap, nexttid, cridmap, nextcrid) (NewCRef old) =-    let cridmap' = I.insert (fromId old) nextcrid cridmap-        nextcrid' = nextcrid + 1-    in ((tidmap, nexttid, cridmap', nextcrid'), NewCRef (toId nextcrid))-  updateAction s@(_, _, cridmap, _) (ReadCRef old) =-    (s, ReadCRef (renumbered cridmap old))-  updateAction s@(_, _, cridmap, _) (ReadCRefCas old) =-    (s, ReadCRefCas (renumbered cridmap old))-  updateAction s@(_, _, cridmap, _) (ModCRef old) =-    (s, ModCRef (renumbered cridmap old))-  updateAction s@(_, _, cridmap, _) (ModCRefCas old) =-    (s, ModCRefCas (renumbered cridmap old))-  updateAction s@(_, _, cridmap, _) (WriteCRef old) =-    (s, WriteCRef (renumbered cridmap old))-  updateAction s@(_, _, cridmap, _) (CasCRef old b) =-    (s, CasCRef (renumbered cridmap old) b)-  updateAction s@(tidmap, _, _, _) (STM tas olds) =-    (s, STM tas (map (renumbered tidmap) olds))-  updateAction s@(tidmap, _, _, _) (ThrowTo old) =-    (s, ThrowTo (renumbered tidmap old))-  updateAction s@(tidmap, _, _, _) (BlockedThrowTo old) =-    (s, BlockedThrowTo (renumbered tidmap old))-  updateAction s act = (s, act)--  renumbered :: (Coercible a Id, Coercible Id a) => I.IntMap Int -> a -> a-  renumbered idmap id_ = toId $ I.findWithDefault (fromId id_) (fromId id_) idmap------------------------------------------------------------------------------------ * Utilities---- | Helper function for constructing IDs of any sort.-toId :: Coercible Id a => Int -> a-toId = coerce . Id Nothing---- | Helper function for deconstructing IDs of any sort.-fromId :: Coercible a Id => a -> Int-fromId a = let (Id _ id_) = coerce a in id_
Test/DejaFu/SCT/Internal/DPOR.hs view
@@ -167,8 +167,7 @@  -- | Add a new trace to the stack.  This won't work if to-dos aren't explored depth-first. incorporateTrace-  :: MemType-  -> Bool+  :: Bool   -- ^ Whether the \"to-do\" point which was used to create this new   -- execution was conservative or not.   -> Trace@@ -176,10 +175,10 @@   -- and the action performed.   -> DPOR   -> DPOR-incorporateTrace memtype conservative trace dpor0 = grow initialDepState (initialDPORThread dpor0) trace dpor0 where+incorporateTrace conservative trace dpor0 = grow initialDepState (initialDPORThread dpor0) trace dpor0 where   grow state tid trc@((d, _, a):rest) dpor =     let tid'   = tidOf tid d-        state' = updateDepState memtype state tid' a+        state' = updateDepState state tid' a     in case dporNext dpor of          Just (t, child)            | t == tid' ->@@ -200,7 +199,7 @@    -- Construct a new subtree corresponding to a trace suffix.   subtree state tid sleep ((_, _, a):rest) = validateDPOR "incorporateTrace (subtree)" $-    let state' = updateDepState memtype state tid a+    let state' = updateDepState state tid a         sleep' = M.filterWithKey (\t a' -> not $ dependent state' tid a t a') sleep     in DPOR         { dporRunnable = S.fromList $ case rest of@@ -234,8 +233,7 @@ -- runnable, a dependency is imposed between this final action and -- everything else. findBacktrackSteps-  :: MemType-  -> BacktrackFunc+  :: BacktrackFunc   -- ^ Backtracking function. Given a list of backtracking points, and   -- a thread to backtrack to at a specific point in that list, add   -- the new backtracking points. There will be at least one: this@@ -252,12 +250,12 @@   -> Trace   -- ^ The execution trace.   -> [BacktrackStep]-findBacktrackSteps memtype backtrack boundKill = go initialDepState S.empty initialThread [] . F.toList where+findBacktrackSteps backtrack boundKill = go initialDepState S.empty initialThread [] . F.toList where   -- Walk through the traces one step at a time, building up a list of   -- new backtracking points.   go state allThreads tid bs ((e,i):is) ((d,_,a):ts) =     let tid' = tidOf tid d-        state' = updateDepState memtype state tid' a+        state' = updateDepState state tid' a         this = BacktrackStep           { bcktThreadid   = tid'           , bcktDecision   = d@@ -465,19 +463,18 @@ -- yielded. Furthermore, threads which /will/ yield are ignored in -- preference of those which will not. dporSched-  :: MemType-  -> IncrementalBoundFunc k+  :: IncrementalBoundFunc k   -- ^ Bound function: returns true if that schedule prefix terminated   -- with the lookahead decision fits within the bound.   -> Scheduler (DPORSchedState k)-dporSched memtype boundf = Scheduler $ \prior threads s ->+dporSched boundf = Scheduler $ \prior threads s ->   let     -- The next scheduler state     nextState rest = s       { schedBPoints  = schedBPoints s |> (restrictToBound fst threads', rest)       , schedDepState = nextDepState       }-    nextDepState = let ds = schedDepState s in maybe ds (uncurry $ updateDepState memtype ds) prior+    nextDepState = let ds = schedDepState s in maybe ds (uncurry $ updateDepState ds) prior      -- Pick a new thread to run, not considering bounds. Choose the     -- current thread if available and it hasn't just yielded,@@ -637,20 +634,18 @@   -- thread and if the actions can be interrupted. We can also   -- slightly improve on that by not considering interrupting the   -- normal termination of a thread: it doesn't make a difference.-  (ThrowTo t, WillStop) | t == t2 -> False-  (Stop, WillThrowTo t) | t == t1 -> False-  (ThrowTo t, _)     | t == t2 -> canInterruptL ds t2 l2-  (_, WillThrowTo t) | t == t1 -> canInterrupt  ds t1 a1+  (ThrowTo t, _)     | t == t2 -> canInterruptL ds t2 l2 && l2 /= WillStop+  (_, WillThrowTo t) | t == t1 -> canInterrupt  ds t1 a1 && a1 /= Stop    -- Another worst-case: assume all STM is dependent.   (STM _ _, WillSTM) -> True   (BlockedSTM _, WillSTM) -> True -  -- This is a bit pessimistic: Set/Get are only dependent if the-  -- value set is not the same as the value that will be got, but we-  -- can't know that here. 'dependent' optimises this case.+  -- the number of capabilities is essentially a global shared+  -- variable   (GetNumCapabilities _, WillSetNumCapabilities _) -> True   (SetNumCapabilities _, WillGetNumCapabilities)   -> True+  (SetNumCapabilities _, WillSetNumCapabilities _) -> True    _ -> dependentActions ds (simplifyAction a1) (simplifyLookahead l2) @@ -659,56 +654,37 @@ -- being so great an over-approximation as to be useless! dependentActions :: DepState -> ActionType -> ActionType -> Bool dependentActions ds a1 a2 = case (a1, a2) of-  -- Unsynchronised reads and writes are always dependent, even under-  -- a relaxed memory model, as an unsynchronised write gives rise to-  -- a commit, which synchronises.-  (UnsynchronisedRead          r1, _) | same crefOf && a2 /= PartiallySynchronisedCommit r1 -> a2 /= UnsynchronisedRead r1-  (UnsynchronisedWrite         r1, _) | same crefOf && a2 /= PartiallySynchronisedCommit r1 -> True-  (PartiallySynchronisedWrite  r1, _) | same crefOf && a2 /= PartiallySynchronisedCommit r1 -> True-  (PartiallySynchronisedModify r1, _) | same crefOf && a2 /= PartiallySynchronisedCommit r1 -> True-  (SynchronisedModify          r1, _) | same crefOf && a2 /= PartiallySynchronisedCommit r1 -> True+  (UnsynchronisedRead _, UnsynchronisedRead _) -> False    -- Unsynchronised writes and synchronisation where the buffer is not   -- empty.   --   -- See [RMMVerification], lemma 5.25.-  (UnsynchronisedWrite r1, PartiallySynchronisedCommit _) | same crefOf && isBuffered ds r1 -> False-  (PartiallySynchronisedCommit _, UnsynchronisedWrite r2) | same crefOf && isBuffered ds r2 -> False+  (UnsynchronisedWrite r1, PartiallySynchronisedCommit r2) | r1 == r2 && isBuffered ds r1 -> False+  (PartiallySynchronisedCommit r1, UnsynchronisedWrite r2) | r1 == r2 && isBuffered ds r1 -> False    -- Unsynchronised reads where a memory barrier would flush a   -- buffered write-  (UnsynchronisedRead r1, _) | isBarrier a2 -> isBuffered ds r1-  (_, UnsynchronisedRead r2) | isBarrier a1 -> isBuffered ds r2+  (UnsynchronisedRead r1, _) | isBarrier a2 && isBuffered ds r1 -> True+  (_, UnsynchronisedRead r2) | isBarrier a1 && isBuffered ds r2 -> True    -- Commits and memory barriers must be dependent, as memory barriers   -- (currently) flush in a consistent order.  Alternative orders need   -- to be explored as well.  Perhaps a better implementation of   -- memory barriers would just block every non-commit thread while   -- any buffer is nonempty.-  (PartiallySynchronisedCommit _, _) | isBarrier a2 -> True-  (_, PartiallySynchronisedCommit _) | isBarrier a1 -> True+  (PartiallySynchronisedCommit r1, _) | synchronises a2 r1 -> True+  (_, PartiallySynchronisedCommit r2) | synchronises a1 r2 -> True    -- Two @MVar@ puts are dependent if they're to the same empty   -- @MVar@, and two takes are dependent if they're to the same full   -- @MVar@.-  (SynchronisedWrite v1, SynchronisedWrite v2) -> v1 == v2 && not (isFull ds v1)-  (SynchronisedRead  v1, SynchronisedRead  v2) -> v1 == v2 && isFull ds v1--  (_, _) -> case getSame crefOf of-    -- Two actions on the same CRef where at least one is synchronised-    Just r -> synchronises a1 r || synchronises a2 r-    -- Two actions on the same MVar-    _ -> same mvarOf--  where-    same :: Eq a => (ActionType -> Maybe a) -> Bool-    same = isJust . getSame+  (SynchronisedWrite v1, SynchronisedWrite v2) | v1 == v2 -> not (isFull ds v1)+  (SynchronisedRead  v1, SynchronisedRead  v2) | v1 == v2 -> isFull ds v1+  (SynchronisedWrite v1, SynchronisedRead  v2) | v1 == v2 -> True+  (SynchronisedRead  v1, SynchronisedWrite v2) | v1 == v2 -> True -    getSame :: Eq a => (ActionType -> Maybe a) -> Maybe a-    getSame f =-      let f1 = f a1-          f2 = f a2-      in if f1 == f2 then f1 else Nothing+  (_, _) -> maybe False (\r -> Just r == crefOf a2) (crefOf a1)  ------------------------------------------------------------------------------- -- ** Dependency function state@@ -737,20 +713,19 @@  -- | Update the dependency state with the action that has just -- happened.-updateDepState :: MemType -> DepState -> ThreadId -> ThreadAction -> DepState-updateDepState memtype depstate tid act = DepState-  { depCRState   = updateCRState memtype act $ depCRState   depstate-  , depMVState   = updateMVState         act $ depMVState   depstate-  , depMaskState = updateMaskState tid   act $ depMaskState depstate+updateDepState :: DepState -> ThreadId -> ThreadAction -> DepState+updateDepState depstate tid act = DepState+  { depCRState   = updateCRState       act $ depCRState   depstate+  , depMVState   = updateMVState       act $ depMVState   depstate+  , depMaskState = updateMaskState tid act $ depMaskState depstate   }  -- | Update the @CRef@ buffer state with the action that has just -- happened.-updateCRState :: MemType -> ThreadAction -> Map CRefId Bool -> Map CRefId Bool-updateCRState SequentialConsistency _ = const M.empty-updateCRState _ (CommitCRef _ r) = M.delete r-updateCRState _ (WriteCRef    r) = M.insert r True-updateCRState _ ta+updateCRState :: ThreadAction -> Map CRefId Bool -> Map CRefId Bool+updateCRState (CommitCRef _ r) = M.delete r+updateCRState (WriteCRef    r) = M.insert r True+updateCRState ta   | isBarrier $ simplifyAction ta = const M.empty   | otherwise = id 
Test/DejaFu/Settings.hs view
@@ -150,44 +150,6 @@    , learlyExit -  -- ** Representative traces--  -- | There may be many different execution traces which give rise to-  -- the same result, but some traces can be more complex than others.-  ---  -- By supplying an equality predicate on results, all but the-  -- simplest trace for each distinct result can be thrown away.-  ---  -- __Slippage:__ Just comparing results can lead to different errors-  -- which happen to have the same result comparing as equal.  For-  -- example, all deadlocks have the same result (@Left Deadlock@),-  -- but may have different causes.  See issue @#241@.--  , lequality--  -- ** Trace simplification--  -- | There may be many ways to reveal the same bug, and dejafu is-  -- not guaranteed to find the simplest way first.  This is-  -- particularly problematic with random testing, where the schedules-  -- generated tend to involve a lot of context switching.-  -- Simplification produces smaller traces, which still have the same-  -- essential behaviour.-  ---  -- __Performance:__ Simplification in dejafu, unlike shrinking in-  -- most random testing tools, is quite cheap.  Simplification is-  -- guaranteed to preserve semantics, so the test case does not need-  -- to be re-run repeatedly during the simplification process.  The-  -- test case is re-run only /once/, after the process completes, for-  -- implementation reasons.-  ---  -- Concurrency tests can be rather large, however.  So-  -- simplification is disabled by default, and it is /highly/-  -- recommended to also use 'lequality', to reduce the number of-  -- traces to simplify.--  , lsimplify-   -- ** Debug output    -- | You can opt to receive debugging messages by setting debugging@@ -235,8 +197,6 @@   , _debugShow = Nothing   , _debugPrint = Nothing   , _earlyExit = Nothing-  , _equality = Nothing-  , _simplify = False   }  -------------------------------------------------------------------------------@@ -406,24 +366,6 @@ -- @since 1.2.0.0 learlyExit :: Lens' (Settings n a) (Maybe (Either Failure a -> Bool)) learlyExit afb s = (\b -> s {_earlyExit = b}) <$> afb (_earlyExit s)------------------------------------------------------------------------------------ Representative traces---- | A lens into the equality predicate.------ @since unreleased-lequality :: Lens' (Settings n a) (Maybe (a -> a -> Bool))-lequality afb s = (\b -> s {_equality = b}) <$> afb (_equality s)------------------------------------------------------------------------------------ Simplification---- | A lens into the simplify flag.------ @since unreleased-lsimplify :: Lens' (Settings n a) Bool-lsimplify afb s = (\b -> s {_simplify = b}) <$> afb (_simplify s)  ------------------------------------------------------------------------------- -- Debug output
Test/DejaFu/Utils.hs view
@@ -1,6 +1,6 @@ -- |--- Module      : Test.DejaFu.Utils--- Copyright   : (c) 2017--2018 Michael Walker+-- Module      : Test.DejaFu.utils+-- Copyright   : (c) 2017 Michael Walker -- License     : MIT -- Maintainer  : Michael Walker <mike@barrucadu.co.uk> -- Stability   : experimental@@ -18,13 +18,6 @@  ------------------------------------------------------------------------------- -- * Traces---- | Turn a 'Trace' into an abbreviated form.------ @since unreleased-toTIdTrace :: Trace -> [(ThreadId, ThreadAction)]-toTIdTrace =-  tail . scanl (\(t, _) (d, _, a) -> (tidOf t d, a)) (initialThread, undefined)  -- | Pretty-print a trace, including a key of the thread IDs (not -- including thread 0). Each line of the key is indented by two
dejafu.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                dejafu-version:             1.3.0.1+version:             1.3.0.2 synopsis:            A library for unit-testing concurrent programs.  description:@@ -33,7 +33,7 @@ source-repository this   type:     git   location: https://github.com/barrucadu/dejafu.git-  tag:      dejafu-1.3.0.1+  tag:      dejafu-1.3.0.2  library   exposed-modules:     Test.DejaFu@@ -52,7 +52,6 @@                      , Test.DejaFu.Conc.Internal.STM                      , Test.DejaFu.Conc.Internal.Threading                      , Test.DejaFu.Internal-                     , Test.DejaFu.SCT.Internal                      , Test.DejaFu.SCT.Internal.DPOR                      , Test.DejaFu.SCT.Internal.Weighted