7
7
{-# LANGUAGE MultiParamTypeClasses #-}
8
8
{-# LANGUAGE NamedFieldPuns #-}
9
9
{-# LANGUAGE RankNTypes #-}
10
+ -- only used to construct records if its clear to do so
11
+ {-# LANGUAGE RecordWildCards #-}
10
12
{-# LANGUAGE ScopedTypeVariables #-}
11
13
{-# LANGUAGE TypeFamilies #-}
12
14
@@ -1641,31 +1643,33 @@ data Step = Step {
1641
1643
}
1642
1644
deriving Show
1643
1645
1644
- -- steps race if they can be reordered with a possibly different outcome
1646
+ -- | Check if two steps can be reordered with a possibly different outcome.
1647
+ --
1645
1648
racingSteps :: Step -- ^ an earlier step
1646
1649
-> Step -- ^ a later step
1647
1650
-> Bool
1648
1651
racingSteps s s' =
1649
- stepThreadId s /= stepThreadId s'
1652
+ -- is s executed by a racy thread
1653
+ isRacyThreadId (stepThreadId s)
1654
+ -- steps which belong to the same thread cannot race
1655
+ && stepThreadId s /= stepThreadId s'
1656
+ -- if s wakes up s' then s and s' cannot race
1650
1657
&& not (stepThreadId s' `elem` effectWakeup (stepEffect s))
1651
- && (stepEffect s `racingEffects` stepEffect s'
1652
- || throwsTo s s'
1653
- || throwsTo s' s)
1658
+ -- s effects races with s' effects or either one throws to the other
1659
+ && ( stepEffect s `racingEffects` stepEffect s'
1660
+ || throwsTo s s'
1661
+ || throwsTo s' s
1662
+ )
1654
1663
where throwsTo s1 s2 =
1655
1664
stepThreadId s2 `elem` effectThrows (stepEffect s1)
1656
1665
&& stepEffect s2 /= mempty
1657
1666
1658
1667
currentStep :: Thread s a -> Step
1659
- currentStep Thread { threadId = tid,
1660
- threadStep = tstep,
1661
- threadEffect = teffect,
1662
- threadVClock = vClock
1663
- } =
1664
- Step { stepThreadId = tid,
1665
- stepStep = tstep,
1666
- stepEffect = teffect,
1667
- stepVClock = vClock
1668
- }
1668
+ currentStep Thread { threadId = stepThreadId,
1669
+ threadStep = stepStep,
1670
+ threadEffect = stepEffect,
1671
+ threadVClock = stepVClock
1672
+ } = Step {.. }
1669
1673
1670
1674
stepThread :: Thread s a -> Thread s a
1671
1675
stepThread thread@ Thread { threadId = tid,
@@ -1678,16 +1682,16 @@ stepThread thread@Thread { threadId = tid,
1678
1682
1679
1683
-- As we run a simulation, we collect info about each previous step
1680
1684
data StepInfo = StepInfo {
1681
- stepInfoStep :: Step ,
1685
+ stepInfoStep :: ! Step ,
1682
1686
-- Control information when we reached this step
1683
- stepInfoControl :: ScheduleControl ,
1687
+ stepInfoControl :: ! ScheduleControl ,
1684
1688
-- threads that are still concurrent with this step
1685
- stepInfoConcurrent :: Set ThreadId ,
1689
+ stepInfoConcurrent :: ! ( Set ThreadId ) ,
1686
1690
-- steps following this one that did not happen after it
1687
1691
-- (in reverse order)
1688
- stepInfoNonDep :: [Step ],
1692
+ stepInfoNonDep :: ! [Step ],
1689
1693
-- later steps that race with this one
1690
- stepInfoRaces :: [Step ]
1694
+ stepInfoRaces :: ! [Step ]
1691
1695
}
1692
1696
deriving Show
1693
1697
@@ -1707,7 +1711,7 @@ noRaces = Races [] []
1707
1711
1708
1712
updateRacesInSimState :: Thread s a -> SimState s a -> Races
1709
1713
updateRacesInSimState thread SimState { control, threads, races } =
1710
- traceRaces $
1714
+ debugTraceRaces $
1711
1715
updateRaces step
1712
1716
(isThreadBlocked thread)
1713
1717
control
@@ -1726,89 +1730,124 @@ updateRacesInSimState thread SimState{ control, threads, races } =
1726
1730
-- concurrent set. When this becomes empty, a step can be retired into
1727
1731
-- the "complete" category, but only if there are some steps racing
1728
1732
-- with it.
1729
- updateRaces :: Step -> Bool -> ScheduleControl -> Set ThreadId -> Races -> Races
1733
+ updateRaces :: Step
1734
+ -- ^ executed step
1735
+ -> Bool
1736
+ -- ^ is the thread blocking
1737
+ -> ScheduleControl
1738
+ -- ^ schedule control
1739
+ -> Set ThreadId
1740
+ -- ^ set of runnable threads which are not forked by the step, and
1741
+ -- thus can race with it.
1742
+ -> Races -> Races
1730
1743
updateRaces newStep@ Step { stepThreadId = tid, stepEffect = newEffect }
1731
1744
blocking
1732
1745
control
1733
- newConcurrent0
1746
+ concurrent0
1734
1747
races@ Races { activeRaces } =
1735
1748
1736
- let justBlocking :: Bool
1737
- justBlocking = blocking && onlyReadEffect newEffect
1738
-
1739
- -- a new step cannot race with any threads that it just woke up
1740
- new :: [StepInfo ]
1741
- ! new | isNotRacyThreadId tid = [] -- non-racy threads do not race
1742
- | Set. null newConcurrent = [] -- cannot race with anything
1743
- | justBlocking = [] -- no need to defer a blocking transaction
1744
- | otherwise =
1745
- [StepInfo { stepInfoStep = newStep,
1746
- stepInfoControl = control,
1747
- stepInfoConcurrent = newConcurrent,
1748
- stepInfoNonDep = [] ,
1749
- stepInfoRaces = []
1750
- }]
1749
+ let -- A new step to add to the `activeRaces` list.
1750
+ newStepInfo :: Maybe StepInfo
1751
+ ! newStepInfo | isNotRacyThreadId tid = Nothing
1752
+ -- non-racy threads do not race
1753
+
1754
+ | Set. null concurrent = Nothing
1755
+ -- cannot race with nothing
1756
+
1757
+ | isBlocking = Nothing
1758
+ -- no need to defer a blocking transaction
1759
+
1760
+ | otherwise =
1761
+ Just $! StepInfo { stepInfoStep = newStep,
1762
+ stepInfoControl = control,
1763
+ stepInfoConcurrent = concurrent,
1764
+ stepInfoNonDep = [] ,
1765
+ stepInfoRaces = []
1766
+ }
1751
1767
where
1752
- newConcurrent :: Set ThreadId
1753
- newConcurrent = foldr Set. delete newConcurrent0 (effectWakeup newEffect)
1768
+ concurrent :: Set ThreadId
1769
+ concurrent = foldr Set. delete concurrent0 (effectWakeup newEffect)
1770
+
1771
+ isBlocking :: Bool
1772
+ isBlocking = blocking && onlyReadEffect newEffect
1773
+
1774
+ -- Used to update each `StepInfo` in `activeRaces`.
1775
+ updateStepInfo :: StepInfo -> StepInfo
1776
+ updateStepInfo stepInfo@ StepInfo { stepInfoStep = step,
1777
+ stepInfoConcurrent = concurrent,
1778
+ stepInfoNonDep,
1779
+ stepInfoRaces } =
1780
+ -- if this step depends on the previous step, or is not concurrent,
1781
+ -- then any threads that it wakes up become non-concurrent also.
1782
+ let ! lessConcurrent = foldr Set. delete concurrent (effectWakeup newEffect) in
1783
+
1784
+ if tid `notElem` concurrent
1785
+ then stepInfo { stepInfoConcurrent = lessConcurrent }
1786
+
1787
+ -- The core of IOSimPOR. Detect if `newStep` is racing with any
1788
+ -- previous steps and update each `StepInfo`.
1789
+ else let theseStepsRace = step `racingSteps` newStep
1790
+ -- `step` happened before `newStep` (`newStep` happened after
1791
+ -- `step`)
1792
+ happensBefore = step `happensBeforeStep` newStep
1793
+
1794
+ -- We will only record the first race with each thread.
1795
+ -- Reversing the first race makes the next race detectable.
1796
+ -- Thus we remove a thread from the concurrent set after the
1797
+ -- first race.
1798
+ ! concurrent'
1799
+ | happensBefore = Set. delete tid lessConcurrent
1800
+ | theseStepsRace = Set. delete tid concurrent
1801
+ | otherwise = concurrent
1802
+
1803
+ ! stepInfoNonDep'
1804
+ -- `newStep` happened after `step`
1805
+ | happensBefore = stepInfoNonDep
1806
+ -- `newStep` did not happen after `step`
1807
+ | otherwise = newStep : stepInfoNonDep
1808
+
1809
+ -- Here we record discovered races. We only record a new
1810
+ -- race if we are following the default schedule, to avoid
1811
+ -- finding the same race in different parts of the search
1812
+ -- space.
1813
+ ! stepInfoRaces'
1814
+ | theseStepsRace && isDefaultSchedule control
1815
+ = newStep : stepInfoRaces
1816
+ | otherwise = stepInfoRaces
1817
+
1818
+ in stepInfo { stepInfoConcurrent = effectForks newEffect
1819
+ `Set.union` concurrent',
1820
+ stepInfoNonDep = stepInfoNonDep',
1821
+ stepInfoRaces = stepInfoRaces'
1822
+ }
1754
1823
1755
1824
activeRaces' :: [StepInfo ]
1756
- ! activeRaces' =
1757
- [ -- if this step depends on the previous step, or is not concurrent,
1758
- -- then any threads that it wakes up become non-concurrent also.
1759
- let ! lessConcurrent = foldr Set. delete concurrent (effectWakeup newEffect) in
1760
- if tid `elem` concurrent then
1761
- let theseStepsRace = isRacyThreadId tid && racingSteps step newStep
1762
- happensBefore = step `happensBeforeStep` newStep
1763
- ! nondep' | happensBefore = nondep
1764
- | otherwise = newStep : nondep
1765
- -- We will only record the first race with each thread---reversing
1766
- -- the first race makes the next race detectable. Thus we remove a
1767
- -- thread from the concurrent set after the first race.
1768
- concurrent' | happensBefore = Set. delete tid lessConcurrent
1769
- | theseStepsRace = Set. delete tid concurrent
1770
- | otherwise = concurrent
1771
- -- Here we record discovered races.
1772
- -- We only record a new race if we are following the default schedule,
1773
- -- to avoid finding the same race in different parts of the search space.
1774
- ! stepRaces' | (control == ControlDefault ||
1775
- control == ControlFollow [] [] ) &&
1776
- theseStepsRace = newStep : stepRaces
1777
- | otherwise = stepRaces
1778
-
1779
- in stepInfo { stepInfoConcurrent = effectForks newEffect
1780
- `Set.union` concurrent',
1781
- stepInfoNonDep = nondep',
1782
- stepInfoRaces = stepRaces'
1783
- }
1825
+ ! activeRaces' =
1826
+ case newStepInfo of
1827
+ Nothing -> updateStepInfo <$> activeRaces
1828
+ Just si -> si : (updateStepInfo <$> activeRaces)
1784
1829
1785
- else stepInfo { stepInfoConcurrent = lessConcurrent }
1830
+ in normalizeRaces races { activeRaces = activeRaces' }
1831
+
1832
+ normalizeRaces :: Races -> Races
1833
+ normalizeRaces Races { activeRaces, completeRaces } =
1834
+ let ! activeRaces' = filter (not . null . stepInfoConcurrent) activeRaces
1835
+ ! completeRaces' = ( filter (not . null . stepInfoRaces)
1836
+ . filter (null . stepInfoConcurrent)
1837
+ $ activeRaces
1838
+ )
1839
+ ++ completeRaces
1840
+ in Races { activeRaces = activeRaces', completeRaces = completeRaces' }
1786
1841
1787
- | ! stepInfo@ StepInfo { stepInfoStep = step,
1788
- stepInfoConcurrent = concurrent,
1789
- stepInfoNonDep = nondep,
1790
- stepInfoRaces = stepRaces
1791
- }
1792
- <- activeRaces ]
1793
- in normalizeRaces $ races { activeRaces = new ++ activeRaces' }
1794
1842
1795
1843
-- When a thread terminates, we remove it from the concurrent thread
1796
1844
-- sets of active races.
1797
-
1798
1845
threadTerminatesRaces :: ThreadId -> Races -> Races
1799
1846
threadTerminatesRaces tid races@ Races { activeRaces } =
1800
1847
let activeRaces' = [ s{stepInfoConcurrent = Set. delete tid stepInfoConcurrent}
1801
1848
| s@ StepInfo { stepInfoConcurrent } <- activeRaces ]
1802
1849
in normalizeRaces $ races{ activeRaces = activeRaces' }
1803
1850
1804
- normalizeRaces :: Races -> Races
1805
- normalizeRaces Races { activeRaces, completeRaces } =
1806
- let ! activeRaces' = filter (not . null . stepInfoConcurrent) activeRaces
1807
- ! completeRaces' = filter (not . null . stepInfoRaces)
1808
- (filter (null . stepInfoConcurrent) activeRaces)
1809
- ++ completeRaces
1810
- in Races { activeRaces = activeRaces', completeRaces = completeRaces' }
1811
-
1812
1851
-- We assume that steps do not race with later steps after a quiescent
1813
1852
-- period. Quiescent periods end when simulated time advances, thus we
1814
1853
-- are assuming here that all work is completed before a timer
@@ -1822,10 +1861,11 @@ quiescentRaces Races{ activeRaces, completeRaces } =
1822
1861
, not (null (stepInfoRaces s))
1823
1862
] ++ completeRaces }
1824
1863
1825
- traceRaces :: Races -> Races
1826
- traceRaces r = r
1827
- -- traceRaces r@Races{activeRaces,completeRaces} =
1828
- -- Debug.trace ("Tracking "++show (length (concatMap stepInfoRaces activeRaces)) ++" races") r
1864
+ -- for debugging purposes
1865
+ debugTraceRaces :: Races -> Races
1866
+ debugTraceRaces r = r
1867
+ -- debugTraceRaces r@Races{activeRaces,completeRaces} =
1868
+ -- Debug.trace ("Tracking "++show (length (concatMap stepInfoRaces activeRaces)) ++" races") r
1829
1869
1830
1870
1831
1871
--
@@ -1954,4 +1994,4 @@ extendScheduleControl control m =
1954
1994
"Extending "++show control,
1955
1995
" with "++show m,
1956
1996
" yields "++show control']) -}
1957
- control'
1997
+ control'
0 commit comments