@@ -23,7 +23,9 @@ module Control.Monad.IOSim
23
23
-- ** Explore races using /IOSimPOR/
24
24
-- $iosimpor
25
25
, exploreSimTrace
26
+ , exploreSimTraceST
26
27
, controlSimTrace
28
+ , controlSimTraceST
27
29
, ScheduleMod (.. )
28
30
, ScheduleControl (.. )
29
31
-- *** Exploration options
@@ -171,6 +173,10 @@ selectTraceRaces = go
171
173
-- unsafe, of course, since that function may return different results
172
174
-- at different times.
173
175
176
+ -- | Detach discovered races. This is written in `ST` monad to support
177
+ -- simulations which do not terminate, in which case we will only detach races
178
+ -- up to the point we evaluated the simulation.
179
+ --
174
180
detachTraceRacesST :: forall a s . SimTrace a -> ST s (ST s [ScheduleControl ], SimTrace a )
175
181
detachTraceRacesST trace0 = do
176
182
races <- newSTRef []
@@ -191,6 +197,15 @@ detachTraceRacesST trace0 = do
191
197
trace <- go trace0
192
198
return (readRaces, trace)
193
199
200
+
201
+ -- | Like `detachTraceRacesST`, but it doesn't expose discovered races.
202
+ --
203
+ detachTraceRaces :: forall a . SimTrace a -> SimTrace a
204
+ detachTraceRaces = Trace. filter (\ a -> case a of
205
+ SimPOREvent { seType = EventRaces {} } -> False
206
+ SimRacesFound {} -> False
207
+ _ -> True )
208
+
194
209
-- | Select all the traced values matching the expected type. This relies on
195
210
-- the sim's dynamic trace facility.
196
211
--
@@ -489,6 +504,8 @@ runSimTrace mainAction = runST (runSimTraceST mainAction)
489
504
-- On property failure it will show the failing schedule (`ScheduleControl`)
490
505
-- which can be plugged to `controlSimTrace`.
491
506
--
507
+ -- Note: `exploreSimTrace` evaluates each schedule in parallel (using `par`).
508
+ --
492
509
exploreSimTrace
493
510
:: forall a test . Testable test
494
511
=> (ExplorationOptions -> ExplorationOptions )
@@ -499,34 +516,51 @@ exploreSimTrace
499
516
-- ^ a callback which receives the previous trace (e.g. before reverting
500
517
-- a race condition) and current trace
501
518
-> Property
502
- exploreSimTrace optsf mainAction k =
503
- case explorationReplay opts of
504
- Nothing ->
505
- case runST (do cacheRef <- createCacheST
506
- prop <- explore cacheRef (explorationScheduleBound opts) (explorationBranching opts) ControlDefault Nothing
507
- size <- cacheSizeST cacheRef
508
- return (prop, size)
509
- ) of
510
- (prop, ! size) -> tabulate " Modified schedules explored" [bucket size] prop
511
-
512
- Just control ->
513
- replaySimTrace opts mainAction control (k Nothing )
519
+ exploreSimTrace optsf main k =
520
+ runST (exploreSimTraceST optsf main (\ a b -> pure (k a b)))
521
+
514
522
523
+ -- | An 'ST' version of 'exploreSimTrace'. The callback also receives
524
+ -- 'ScheduleControl'. This is mostly useful for testing /IOSimPOR/ itself.
525
+ --
526
+ -- Note: `exploreSimTraceST` evaluates each schedule sequentially.
527
+ --
528
+ exploreSimTraceST
529
+ :: forall s a test . Testable test
530
+ => (ExplorationOptions -> ExplorationOptions )
531
+ -> (forall s . IOSim s a )
532
+ -> (Maybe (SimTrace a ) -> SimTrace a -> ST s test )
533
+ -> ST s Property
534
+ exploreSimTraceST optsf main k =
535
+ case explorationReplay opts of
536
+ Just control -> do
537
+ trace <- controlSimTraceST (explorationStepTimelimit opts) control main
538
+ counterexample (" Schedule control: " ++ show control)
539
+ <$> k Nothing trace
540
+ Nothing -> do
541
+ cacheRef <- createCacheST
542
+ prop <- go cacheRef (explorationScheduleBound opts)
543
+ (explorationBranching opts)
544
+ ControlDefault Nothing
545
+ ! size <- cacheSizeST cacheRef
546
+ return $ tabulate " Modified schedules explored" [bucket size] prop
515
547
where
516
548
opts = optsf stdExplorationOptions
517
549
518
- explore :: forall s .
519
- STRef s (Set ScheduleControl )
520
- -> Int -- schedule bound
521
- -> Int -- branching factor
522
- -> ScheduleControl -> Maybe (SimTrace a ) -> ST s Property
523
- explore cacheRef n m control passingTrace = do
524
- traceWithRaces <- IOSimPOR. controlSimTraceST (explorationStepTimelimit opts) control mainAction
550
+ go :: STRef s (Set ScheduleControl )
551
+ -> Int -- schedule bound
552
+ -> Int -- branching factor
553
+ -> ScheduleControl
554
+ -> Maybe (SimTrace a )
555
+ -> ST s Property
556
+ go cacheRef n m control passingTrace = do
557
+ traceWithRaces <- IOSimPOR. controlSimTraceST (explorationStepTimelimit opts) control main
525
558
(readRaces, trace0) <- detachTraceRacesST traceWithRaces
526
559
(readSleeperST, trace) <- compareTracesST passingTrace trace0
527
560
conjoinNoCatchST
528
561
[ do sleeper <- readSleeperST
529
562
() <- traceDebugLog (explorationDebugLevel opts) traceWithRaces
563
+ prop <- k passingTrace trace
530
564
return $ counterexample (" Schedule control: " ++ show control)
531
565
$ counterexample
532
566
(case sleeper of
@@ -538,7 +572,7 @@ exploreSimTrace optsf mainAction k =
538
572
" \n until after:\n " ++
539
573
unlines (map ((" " ++ ). showThread) $ Set. toList racing)
540
574
)
541
- $ k passingTrace trace
575
+ prop
542
576
, do let limit = (n+ m- 1 ) `div` m
543
577
-- To ensure the set of schedules explored is deterministic, we
544
578
-- filter out cached ones *after* selecting the children of this
@@ -550,8 +584,9 @@ exploreSimTrace optsf mainAction k =
550
584
-- tabulate "Races explored" (map show races) $
551
585
tabulate " Branching factor" [bucket branching]
552
586
. tabulate " Race reversals per schedule" [bucket (raceReversals control)]
553
- <$> conjoinParST
554
- [ explore cacheRef n' ((m- 1 ) `max` 1 ) r (Just trace0)
587
+ . conjoin
588
+ <$> sequence
589
+ [ go cacheRef n' ((m- 1 ) `max` 1 ) r (Just trace0)
555
590
| (r,n') <- zip races (divide (n- branching) branching) ]
556
591
]
557
592
@@ -609,28 +644,6 @@ traceDebugLog _ trace = Debug.traceM $ "Simulation trace with discovered schedul
609
644
++ Trace. ppTrace show (ppSimEvent 0 0 0 ) (void `first` trace)
610
645
611
646
612
-
613
- -- | A specialised version of `controlSimTrace'.
614
- --
615
- -- An internal function.
616
- --
617
- replaySimTrace :: forall a test . (Testable test )
618
- => ExplorationOptions
619
- -- ^ race exploration options
620
- -> (forall s . IOSim s a )
621
- -> ScheduleControl
622
- -- ^ a schedule control to reproduce
623
- -> (SimTrace a -> test )
624
- -- ^ a callback which receives the simulation trace. The trace
625
- -- will not contain any race events
626
- -> Property
627
- replaySimTrace opts mainAction control k =
628
- let trace = runST $ do
629
- (_readRaces, trace) <- IOSimPOR. controlSimTraceST (explorationStepTimelimit opts) control mainAction
630
- >>= detachTraceRacesST
631
- return (ignoreRaces trace)
632
- in property (k trace)
633
-
634
647
-- | Run a simulation using a given schedule. This is useful to reproduce
635
648
-- failing cases without exploring the races.
636
649
--
@@ -650,7 +663,7 @@ controlSimTrace limit control main =
650
663
651
664
controlSimTraceST :: Maybe Int -> ScheduleControl -> IOSim s a -> ST s (SimTrace a )
652
665
controlSimTraceST limit control main =
653
- ignoreRaces <$> IOSimPOR. controlSimTraceST limit control main
666
+ detachTraceRaces <$> IOSimPOR. controlSimTraceST limit control main
654
667
655
668
656
669
--
0 commit comments