packages feed

dejafu 0.7.3.0 → 0.8.0.0

raw patch · 9 files changed

+212/−187 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Test.DejaFu.Schedule: type Scheduler state = [(Decision, ThreadAction)] -> Maybe (ThreadId, ThreadAction) -> NonEmpty (ThreadId, Lookahead) -> state -> (Maybe ThreadId, state)
+ Test.DejaFu.Schedule: Scheduler :: (Maybe (ThreadId, ThreadAction) -> NonEmpty (ThreadId, Lookahead) -> state -> (Maybe ThreadId, state)) -> Scheduler state
+ Test.DejaFu.Schedule: [scheduleThread] :: Scheduler state -> Maybe (ThreadId, ThreadAction) -> NonEmpty (ThreadId, Lookahead) -> state -> (Maybe ThreadId, state)
+ Test.DejaFu.Schedule: newtype Scheduler state
- Test.DejaFu.Common: TNew :: TAction
+ Test.DejaFu.Common: TNew :: TVarId -> TAction
- Test.DejaFu.Common: type Trace = [(Decision, [(ThreadId, NonEmpty Lookahead)], ThreadAction)]
+ Test.DejaFu.Common: type Trace = [(Decision, [(ThreadId, Lookahead)], ThreadAction)]
- Test.DejaFu.Conc: type Trace = [(Decision, [(ThreadId, NonEmpty Lookahead)], ThreadAction)]
+ Test.DejaFu.Conc: type Trace = [(Decision, [(ThreadId, Lookahead)], ThreadAction)]
- Test.DejaFu.Conc.Internal: type SeqTrace = Seq (Decision, [(ThreadId, NonEmpty Lookahead)], ThreadAction)
+ Test.DejaFu.Conc.Internal: type SeqTrace = Seq (Decision, [(ThreadId, Lookahead)], ThreadAction)
- Test.DejaFu.Conc.Internal.Common: lookahead :: Action n r -> NonEmpty Lookahead
+ Test.DejaFu.Conc.Internal.Common: lookahead :: Action n r -> Lookahead
- Test.DejaFu.STM: TNew :: TAction
+ Test.DejaFu.STM: TNew :: TVarId -> TAction

Files

CHANGELOG.markdown view
@@ -7,6 +7,29 @@ *de facto* standard Haskell versioning scheme.  +0.8.0.0+-------++- **Date**    2017-09-26+- **Git tag** [dejafu-0.8.0.0][]+- **Hackage** https://hackage.haskell.org/package/dejafu-0.8.0.0++### Test.DejaFu.Common++- Execution traces now only include a single item of lookahead (#120).+- STM traces now include IDs of created `TVar`s (#80).++### Test.DejaFu.Schedule++- Schedulers no longer take the execution trace so far (#106).+- The `Scheduler` type is now a newtype (#122).++[dejafu-0.8.0.0]: https://github.com/barrucadu/dejafu/releases/tag/dejafu-0.8.0.0+++---------------------------------------------------------------------------------------------------++ 0.7.3.0 ------- 
Test/DejaFu/Common.hs view
@@ -63,14 +63,13 @@   , runRefCont   ) where -import           Control.DeepSeq    (NFData(..))-import           Control.Exception  (Exception(..), MaskingState(..))-import           Control.Monad.Ref  (MonadRef(..))-import           Data.List          (intercalate)-import           Data.List.NonEmpty (NonEmpty)-import           Data.Maybe         (fromMaybe, mapMaybe)-import           Data.Set           (Set)-import qualified Data.Set           as S+import           Control.DeepSeq   (NFData(..))+import           Control.Exception (Exception(..), MaskingState(..))+import           Control.Monad.Ref (MonadRef(..))+import           Data.List         (intercalate)+import           Data.Maybe        (fromMaybe, mapMaybe)+import           Data.Set          (Set)+import qualified Data.Set          as S  ------------------------------------------------------------------------------- -- Identifiers@@ -702,9 +701,9 @@  -- | All the actions that an STM transaction can perform. ----- @since 0.4.0.0+-- @since 0.8.0.0 data TAction =-    TNew+    TNew TVarId   -- ^ Create a new @TVar@   | TRead  TVarId   -- ^ Read from a @TVar@.@@ -741,9 +740,9 @@ -- decisions made, all the runnable threads and what they would do, -- and the action a thread took in its step. ----- @since 0.5.0.0+-- @since 0.8.0.0 type Trace-  = [(Decision, [(ThreadId, NonEmpty Lookahead)], ThreadAction)]+  = [(Decision, [(ThreadId, Lookahead)], ThreadAction)]  -- | Scheduling decisions are based on the state of the running -- program, and so we can capture some of that state in recording what
Test/DejaFu/Conc.hs view
@@ -190,13 +190,13 @@ -- nonexistent thread. In either of those cases, the computation will -- be halted. ----- @since 0.6.0.0+-- @since 0.8.0.0 runConcurrent :: MonadRef r n-              => Scheduler s-              -> MemType-              -> s-              -> ConcT r n a-              -> n (Either Failure a, s, Trace)+  => Scheduler s+  -> MemType+  -> s+  -> ConcT r n a+  -> n (Either Failure a, s, Trace) runConcurrent sched memtype s ma = do   (res, ctx, trace, _) <- runConcurrency sched memtype s initialIdSource 2 (unC ma)   pure (res, cSchedState ctx, F.toList trace)
Test/DejaFu/Conc/Internal.hs view
@@ -19,10 +19,9 @@                                                       toException) import           Control.Monad.Ref                   (MonadRef, newRef, readRef,                                                       writeRef)-import qualified Data.Foldable                       as F import           Data.Functor                        (void) import           Data.List                           (sortOn)-import           Data.List.NonEmpty                  (NonEmpty(..), fromList)+import           Data.List.NonEmpty                  (fromList) import qualified Data.Map.Strict                     as M import           Data.Maybe                          (fromJust, isJust) import           Data.Monoid                         ((<>))@@ -42,7 +41,7 @@  -- | 'Trace' but as a sequence. type SeqTrace-  = Seq (Decision, [(ThreadId, NonEmpty Lookahead)], ThreadAction)+  = Seq (Decision, [(ThreadId, Lookahead)], ThreadAction)  -- | Run a concurrent computation with a given 'Scheduler' and initial -- state, returning a failure reason on error. Also returned is the@@ -78,11 +77,13 @@  -- | Run a collection of threads, until there are no threads left. runThreads :: MonadRef r n-           => Scheduler g -> MemType -> r (Maybe (Either Failure a)) -> Context n r g -> n (Context n r g, SeqTrace, Maybe (ThreadId, ThreadAction))-runThreads sched memtype ref = go Seq.empty [] Nothing where-  -- sofar is the 'SeqTrace', sofarSched is the @[(Decision,-  -- ThreadAction)]@ trace the scheduler needs.-  go sofar sofarSched prior ctx+  => Scheduler g+  -> MemType+  -> r (Maybe (Either Failure a))+  -> Context n r g+  -> n (Context n r g, SeqTrace, Maybe (ThreadId, ThreadAction))+runThreads sched memtype ref = go Seq.empty Nothing where+  go sofar prior ctx     | isTerminated  = pure (ctx, sofar, prior)     | isDeadlocked  = die sofar prior Deadlock ctx     | isSTMLocked   = die sofar prior STMDeadlock ctx@@ -96,7 +97,7 @@              Nothing -> die sofar prior InternalError ctx'            Nothing -> die sofar prior Abort ctx'     where-      (choice, g')  = sched sofarSched prior (fromList $ map (\(t,l:|_) -> (t,l)) runnable') (cSchedState ctx)+      (choice, g')  = scheduleThread sched prior (fromList runnable') (cSchedState ctx)       runnable'     = [(t, lookahead (_continuation a)) | (t, a) <- sortOn fst $ M.assocs runnable]       runnable      = M.filter (not . isBlocked) threadsc       threadsc      = addCommitThreads (cWriteBuf ctx) threads@@ -121,17 +122,16 @@        step chosen thread ctx' = do           (res, actOrTrc) <- stepThread sched memtype chosen (_continuation thread) $ ctx { cSchedState = g' }-          let trc         = getTrc actOrTrc-          let sofar'      = sofar      <> trc-          let sofarSched' = sofarSched <> map (\(d,_,a) -> (d,a)) (F.toList trc)-          let prior'      = getPrior actOrTrc+          let trc    = getTrc actOrTrc+          let sofar' = sofar <> trc+          let prior' = getPrior actOrTrc           case res of             Right ctx'' ->               let threads' = if (interruptible <$> M.lookup chosen (cThreads ctx'')) /= Just False                              then unblockWaitingOn chosen (cThreads ctx'')                              else cThreads ctx''                   ctx''' = ctx'' { cThreads = delCommitThreads threads' }-              in go sofar' sofarSched' prior' ctx'''+              in go sofar' prior' ctx'''             Left failure ->               let ctx'' = ctx' { cThreads = delCommitThreads threads }               in die sofar' prior' failure ctx''
Test/DejaFu/Conc/Internal/Common.hs view
@@ -15,7 +15,6 @@ module Test.DejaFu.Conc.Internal.Common where  import           Control.Exception  (Exception, MaskingState(..))-import           Data.List.NonEmpty (NonEmpty, fromList) import           Data.Map.Strict    (Map) import           Test.DejaFu.Common import           Test.DejaFu.STM    (STMLike)@@ -153,37 +152,36 @@ -- * Scheduling & Traces  -- | Look as far ahead in the given continuation as possible.-lookahead :: Action n r -> NonEmpty Lookahead-lookahead = fromList . lookahead' where-  lookahead' (AFork _ _ _)           = [WillFork]-  lookahead' (AMyTId _)              = [WillMyThreadId]-  lookahead' (AGetNumCapabilities _) = [WillGetNumCapabilities]-  lookahead' (ASetNumCapabilities i k) = WillSetNumCapabilities i : lookahead' k-  lookahead' (ANewMVar _ _)           = [WillNewMVar]-  lookahead' (APutMVar (MVar c _) _ k)    = WillPutMVar c : lookahead' k-  lookahead' (ATryPutMVar (MVar c _) _ _) = [WillTryPutMVar c]-  lookahead' (AReadMVar (MVar c _) _)     = [WillReadMVar c]-  lookahead' (ATryReadMVar (MVar c _) _)  = [WillTryReadMVar c]-  lookahead' (ATakeMVar (MVar c _) _)     = [WillTakeMVar c]-  lookahead' (ATryTakeMVar (MVar c _) _)  = [WillTryTakeMVar c]-  lookahead' (ANewCRef _ _ _)         = [WillNewCRef]-  lookahead' (AReadCRef (CRef r _) _)     = [WillReadCRef r]-  lookahead' (AReadCRefCas (CRef r _) _)  = [WillReadCRefCas r]-  lookahead' (AModCRef (CRef r _) _ _)    = [WillModCRef r]-  lookahead' (AModCRefCas (CRef r _) _ _) = [WillModCRefCas r]-  lookahead' (AWriteCRef (CRef r _) _ k) = WillWriteCRef r : lookahead' k-  lookahead' (ACasCRef (CRef r _) _ _ _) = [WillCasCRef r]-  lookahead' (ACommit t c)           = [WillCommitCRef t c]-  lookahead' (AAtom _ _)             = [WillSTM]-  lookahead' (AThrow _)              = [WillThrow]-  lookahead' (AThrowTo tid _ k)      = WillThrowTo tid : lookahead' k-  lookahead' (ACatching _ _ _)       = [WillCatching]-  lookahead' (APopCatching k)        = WillPopCatching : lookahead' k-  lookahead' (AMasking ms _ _)       = [WillSetMasking False ms]-  lookahead' (AResetMask b1 b2 ms k) = (if b1 then WillSetMasking else WillResetMasking) b2 ms : lookahead' k-  lookahead' (ALift _)               = [WillLiftIO]-  lookahead' (AYield k)              = WillYield : lookahead' k-  lookahead' (AReturn k)             = WillReturn : lookahead' k-  lookahead' (AStop _)               = [WillStop]-  lookahead' (ASub _ _)              = [WillSubconcurrency]-  lookahead' (AStopSub k)            = WillStopSubconcurrency : lookahead' k+lookahead :: Action n r -> Lookahead+lookahead (AFork _ _ _) = WillFork+lookahead (AMyTId _) = WillMyThreadId+lookahead (AGetNumCapabilities _) = WillGetNumCapabilities+lookahead (ASetNumCapabilities i _) = WillSetNumCapabilities i+lookahead (ANewMVar _ _) = WillNewMVar+lookahead (APutMVar (MVar c _) _ _) = WillPutMVar c+lookahead (ATryPutMVar (MVar c _) _ _) = WillTryPutMVar c+lookahead (AReadMVar (MVar c _) _) = WillReadMVar c+lookahead (ATryReadMVar (MVar c _) _) = WillTryReadMVar c+lookahead (ATakeMVar (MVar c _) _) = WillTakeMVar c+lookahead (ATryTakeMVar (MVar c _) _) = WillTryTakeMVar c+lookahead (ANewCRef _ _ _) = WillNewCRef+lookahead (AReadCRef (CRef r _) _) = WillReadCRef r+lookahead (AReadCRefCas (CRef r _) _) = WillReadCRefCas r+lookahead (AModCRef (CRef r _) _ _) = WillModCRef r+lookahead (AModCRefCas (CRef r _) _ _) = WillModCRefCas r+lookahead (AWriteCRef (CRef r _) _ _) = WillWriteCRef r+lookahead (ACasCRef (CRef r _) _ _ _) = WillCasCRef r+lookahead (ACommit t c) = WillCommitCRef t c+lookahead (AAtom _ _) = WillSTM+lookahead (AThrow _) = WillThrow+lookahead (AThrowTo tid _ _) = WillThrowTo tid+lookahead (ACatching _ _ _) = WillCatching+lookahead (APopCatching _) = WillPopCatching+lookahead (AMasking ms _ _) = WillSetMasking False ms+lookahead (AResetMask b1 b2 ms _) = (if b1 then WillSetMasking else WillResetMasking) b2 ms+lookahead (ALift _) = WillLiftIO+lookahead (AYield _) = WillYield+lookahead (AReturn _) = WillReturn+lookahead (AStop _) = WillStop+lookahead (ASub _ _) = WillSubconcurrency+lookahead (AStopSub _) = WillStopSubconcurrency
Test/DejaFu/SCT/Internal.hs view
@@ -31,8 +31,7 @@ import           System.Random        (RandomGen, randomR)  import           Test.DejaFu.Common-import           Test.DejaFu.Schedule (Decision(..), Scheduler, decisionOf,-                                       tidOf)+import           Test.DejaFu.Schedule (Scheduler(..), decisionOf, tidOf)  ------------------------------------------------------------------------------- -- * Dynamic partial-order reduction@@ -440,9 +439,70 @@   -- ^ Bound function: returns true if that schedule prefix terminated   -- with the lookahead decision fits within the bound.   -> Scheduler (DPORSchedState k)-dporSched memtype boundf _ prior threads s = schedule where-  -- Pick a thread to run.-  schedule = case schedPrefix s of+dporSched memtype 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 ds) prior++    -- Pick a new thread to run, not considering bounds. Choose the+    -- current thread if available and it hasn't just yielded,+    -- otherwise add all runnable threads.+    initialise = tryDaemons . yieldsToEnd $ case prior of+      Just (tid, act)+        | not (didYield act) && tid `elem` tids -> [tid]+      _ -> tids++    -- If one of the chosen actions will kill the computation, and+    -- there are daemon threads, try them instead.+    --+    -- This is necessary if the killing action is NOT dependent with+    -- every other action, according to the dependency function. This+    -- is, strictly speaking, wrong; an action that kills another+    -- thread is definitely dependent with everything in that+    -- thread. HOWEVER, implementing it that way leads to an explosion+    -- of schedules tried. Really, all that needs to happen is for the+    -- thread-that-would-be-killed to be executed fully ONCE, and then+    -- the normal dependency mechanism will identify any other+    -- backtracking points that should be tried. This is achieved by+    -- adding every thread that would be killed to the to-do list.+    -- Furthermore, these threads MUST be ahead of the killing thread,+    -- or the killing thread will end up in the sleep set and so the+    -- killing action not performed. This is, again, because of the+    -- lack of the dependency messing things up in the name of+    -- performance.+    --+    -- See commits a056f54 and 8554ce9, and my 4th June comment in+    -- issue #52.+    tryDaemons ts+      | any doesKill ts = case partition doesKill tids of+          (kills, nokills) -> nokills ++ kills+      | otherwise = ts+    doesKill t = killsDaemons t (action t)++    -- Restrict the possible decisions to those in the bound.+    restrictToBound f =+      filter (\x -> let t = f x in isJust $ boundf (schedBState s) prior (decision t, action t))++    -- Move the threads which will immediately yield to the end of the list+    yieldsToEnd ts = case partition (willYield . action) ts of+      (yields, noyields) -> noyields ++ yields++    -- Get the decision that will lead to a thread being scheduled.+    decision = decisionOf (fst <$> prior) (S.fromList tids)++    -- Get the action of a thread+    action t = fromJust $ lookup t threads'++    -- The runnable thread IDs+    tids = map fst threads'++    -- The runnable threads as a normal list.+    threads' = toList threads+  in case schedPrefix s of     -- If there is a decision available, make it     (t:ts) ->       let bstate' = boundf (schedBState s) prior (decision t, action t)@@ -461,71 +521,11 @@           signore' = not (null choices) && all (`elem` M.keys ssleep') choices           sbkill'  = not (null initialise) && null choices       in case choices' of-            (nextTid:rest) ->-              let bstate' = boundf (schedBState s) prior (decision nextTid, action nextTid)-              in (Just nextTid, (nextState rest) { schedSleep = ssleep', schedBState = bstate' })-            [] ->-              (Nothing, (nextState []) { schedIgnore = signore', schedBoundKill = sbkill', schedBState = Nothing })--  -- 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 ds) prior--  -- Pick a new thread to run, not considering bounds. Choose the-  -- current thread if available and it hasn't just yielded, otherwise-  -- add all runnable threads.-  initialise = tryDaemons . yieldsToEnd $ case prior of-    Just (tid, act)-      | not (didYield act) && tid `elem` tids -> [tid]-    _ -> tids--  -- If one of the chosen actions will kill the computation, and there-  -- are daemon threads, try them instead.-  ---  -- This is necessary if the killing action is NOT dependent with-  -- every other action, according to the dependency function. This-  -- is, strictly speaking, wrong; an action that kills another thread-  -- is definitely dependent with everything in that thread. HOWEVER,-  -- implementing it that way leads to an explosion of schedules-  -- tried. Really, all that needs to happen is for the-  -- thread-that-would-be-killed to be executed fully ONCE, and then-  -- the normal dependency mechanism will identify any other-  -- backtracking points that should be tried. This is achieved by-  -- adding every thread that would be killed to the to-do list.-  -- Furthermore, these threads MUST be ahead of the killing thread,-  -- or the killing thread will end up in the sleep set and so the-  -- killing action not performed. This is, again, because of the lack-  -- of the dependency messing things up in the name of performance.-  ---  -- See commits a056f54 and 8554ce9, and my 4th June comment in issue-  -- #52.-  tryDaemons ts-    | any doesKill ts = case partition doesKill tids of-        (kills, nokills) -> nokills ++ kills-    | otherwise = ts-  doesKill t = killsDaemons t (action t)--  -- Restrict the possible decisions to those in the bound.-  restrictToBound f = filter (\x -> let t = f x in isJust $ boundf (schedBState s) prior (decision t, action t))--  -- Move the threads which will immediately yield to the end of the list-  yieldsToEnd ts = case partition (willYield . action) ts of-    (yields, noyields) -> noyields ++ yields--  -- Get the decision that will lead to a thread being scheduled.-  decision = decisionOf (fst <$> prior) (S.fromList tids)--  -- Get the action of a thread-  action t = fromJust $ lookup t threads'--  -- The runnable thread IDs-  tids = map fst threads'--  -- The runnable threads as a normal list.-  threads' = toList threads+        (nextTid:rest) ->+          let bstate' = boundf (schedBState s) prior (decision nextTid, action nextTid)+          in (Just nextTid, (nextState rest) { schedSleep = ssleep', schedBState = bstate' })+        [] ->+          (Nothing, (nextState []) { schedIgnore = signore', schedBoundKill = sbkill', schedBState = Nothing })  ------------------------------------------------------------------------------- -- Weighted random scheduler@@ -551,23 +551,25 @@ -- and makes a weighted random choice out of the runnable threads at -- every step. randSched :: RandomGen g => (g -> (Int, g)) -> Scheduler (RandSchedState g)-randSched weightf _ _ threads s = (pick choice enabled, RandSchedState weights' g'') where-  -- Select a thread-  pick idx ((x, f):xs)-    | idx < f = Just x-    | otherwise = pick (idx - f) xs-  pick _ [] = Nothing-  (choice, g'') = randomR (0, sum (map snd enabled) - 1) g'-  enabled = M.toList $ M.filterWithKey (\tid _ -> tid `elem` tids) weights'+randSched weightf = Scheduler $ \_ threads s ->+  let+    -- Select a thread+    pick idx ((x, f):xs)+      | idx < f = Just x+      | otherwise = pick (idx - f) xs+    pick _ [] = Nothing+    (choice, g'') = randomR (0, sum (map snd enabled) - 1) g'+    enabled = M.toList $ M.filterWithKey (\tid _ -> tid `elem` tids) weights' -  -- The weights, with any new threads added.-  (weights', g') = foldr assignWeight (M.empty, schedGen s) tids-  assignWeight tid ~(ws, g0) =-    let (w, g) = maybe (weightf g0) (,g0) (M.lookup tid (schedWeights s))-    in (M.insert tid w ws, g)+    -- The weights, with any new threads added.+    (weights', g') = foldr assignWeight (M.empty, schedGen s) tids+    assignWeight tid ~(ws, g0) =+      let (w, g) = maybe (weightf g0) (,g0) (M.lookup tid (schedWeights s))+      in (M.insert tid w ws, g) -  -- The runnable threads.-  tids = map fst (toList threads)+    -- The runnable threads.+    tids = map fst (toList threads)+  in (pick choice enabled, RandSchedState weights' g'')  ------------------------------------------------------------------------------- -- Dependency function
Test/DejaFu/STM/Internal.hs view
@@ -201,7 +201,7 @@       let (idsource', tvid) = nextTVId n idsource       ref <- newRef a       let tvar = TVar (tvid, ref)-      pure (c tvar, nothing, idsource', [], [tvid], TNew)+      pure (c tvar, nothing, idsource', [], [tvid], TNew tvid)      stepOrElse a b c = cases TOrElse a c       (\trace   -> transaction (TOrElse trace . Just) b c)
Test/DejaFu/Schedule.hs view
@@ -9,7 +9,7 @@ -- Scheduling for concurrent computations. module Test.DejaFu.Schedule   ( -- * Scheduling-    Scheduler+    Scheduler(..)    , Decision(..)   , tidOf@@ -37,25 +37,24 @@ -- | A @Scheduler@ drives the execution of a concurrent program. The -- parameters it takes are: ----- 1. The trace so far.------ 2. The last thread executed (if this is the first invocation, this+-- 1. The last thread executed (if this is the first invocation, this --    is @Nothing@). ----- 3. The runnable threads at this point.+-- 2. The runnable threads at this point. ----- 4. The state.+-- 3. The state. -- -- It returns a thread to execute, or @Nothing@ if execution should -- abort here, and also a new state. ----- @since 0.5.0.0-type Scheduler state-  = [(Decision, ThreadAction)]-  -> Maybe (ThreadId, ThreadAction)-  -> NonEmpty (ThreadId, Lookahead)-  -> state-  -> (Maybe ThreadId, state)+-- @since 0.8.0.0+newtype Scheduler state = Scheduler+  { scheduleThread+    :: Maybe (ThreadId, ThreadAction)+    -> NonEmpty (ThreadId, Lookahead)+    -> state+    -> (Maybe ThreadId, state)+  }  ------------------------------------------------------------------------------- -- Scheduling decisions@@ -93,24 +92,28 @@ -- | A simple random scheduler which, at every step, picks a random -- thread to run. ----- @since 0.5.0.0+-- @since 0.8.0.0 randomSched :: RandomGen g => Scheduler g-randomSched _ _ threads g = (Just $ threads' !! choice, g') where-  (choice, g') = randomR (0, length threads' - 1) g-  threads' = map fst $ toList threads+randomSched = Scheduler go where+  go _ threads g =+    let threads' = map fst (toList threads)+        (choice, g') = randomR (0, length threads' - 1) g+    in (Just $ threads' !! choice, g')  -- | A round-robin scheduler which, at every step, schedules the -- thread with the next 'ThreadId'. ----- @since 0.5.0.0+-- @since 0.8.0.0 roundRobinSched :: Scheduler ()-roundRobinSched _ Nothing ((tid,_):|_) _ = (Just tid, ())-roundRobinSched _ (Just (prior, _)) threads _-  | prior >= maximum threads' = (Just $ minimum threads', ())-  | otherwise = (Just . minimum $ filter (>prior) threads', ())--  where-    threads' = map fst $ toList threads+roundRobinSched = Scheduler go where+  go Nothing ((tid,_):|_) _ = (Just tid, ())+  go (Just (prior, _)) threads _ =+    let threads' = map fst (toList threads)+        candidates =+          if prior >= maximum threads'+          then threads'+          else filter (>prior) threads'+    in (Just (minimum candidates), ())  ------------------------------------------------------------------------------- -- Non-preemptive@@ -119,14 +122,14 @@ -- thread. That is, if the last thread scheduled is still runnable, -- run that, otherwise schedule randomly. ----- @since 0.5.0.0+-- @since 0.8.0.0 randomSchedNP :: RandomGen g => Scheduler g randomSchedNP = makeNonPreemptive randomSched  -- | A round-robin scheduler which doesn't preempt the running -- thread. ----- @since 0.5.0.0+-- @since 0.8.0.0 roundRobinSchedNP :: Scheduler () roundRobinSchedNP = makeNonPreemptive roundRobinSched @@ -136,10 +139,10 @@ -- | Turn a potentially preemptive scheduler into a non-preemptive -- one. ----- @since 0.5.0.0+-- @since 0.8.0.0 makeNonPreemptive :: Scheduler s -> Scheduler s-makeNonPreemptive sched = newsched where-  newsched trc p@(Just (prior, _)) threads s+makeNonPreemptive sched = Scheduler newsched where+  newsched p@(Just (prior, _)) threads s     | prior `elem` map fst (toList threads) = (Just prior, s)-    | otherwise = sched trc p threads s-  newsched trc Nothing threads s = sched trc Nothing threads s+    | otherwise = scheduleThread sched p threads s+  newsched Nothing threads s = scheduleThread sched Nothing threads s
dejafu.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                dejafu-version:             0.7.3.0+version:             0.8.0.0 synopsis:            Systematic testing for Haskell concurrency.  description:@@ -37,7 +37,7 @@ source-repository this   type:     git   location: https://github.com/barrucadu/dejafu.git-  tag:      dejafu-0.7.3.0+  tag:      dejafu-0.8.0.0  library   exposed-modules:     Test.DejaFu