packages feed

io-sim 1.6.0.0 → 1.8.0.0

raw patch · 20 files changed

+518/−365 lines, 20 filesdep +hashabledep ~basedep ~io-classesPVP ok

version bump matches the API change (PVP)

Dependencies added: hashable

Dependency ranges changed: base, io-classes

API changes (from Hackage documentation)

- Control.Monad.IOSim: instance GHC.Exception.Type.Exception Control.Monad.IOSim.Failure
- Control.Monad.IOSim: instance GHC.Show.Show Control.Monad.IOSim.Failure
- Data.List.Trace: instance (GHC.Show.Show b, GHC.Show.Show a) => GHC.Show.Show (Data.List.Trace.Trace a b)
- Data.List.Trace: instance GHC.Base.Functor (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => Control.Monad.Fail.MonadFail (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => Control.Monad.Fix.MonadFix (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => GHC.Base.Alternative (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => GHC.Base.Applicative (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => GHC.Base.Monad (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => GHC.Base.MonadPlus (Data.List.Trace.Trace a)
- Data.List.Trace: instance GHC.Base.Monoid a => GHC.Base.Monoid (Data.List.Trace.Trace a b)
- Data.List.Trace: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Data.List.Trace.Trace a b)
- Data.List.Trace: instance GHC.Show.Show a => Data.Functor.Classes.Show1 (Data.List.Trace.Trace a)
+ Control.Monad.IOSim: Time :: DiffTime -> Time
+ Control.Monad.IOSim: instance GHC.Internal.Exception.Type.Exception Control.Monad.IOSim.Failure
+ Control.Monad.IOSim: instance GHC.Internal.Show.Show Control.Monad.IOSim.Failure
+ Control.Monad.IOSim: newtype Time
+ Data.List.Trace: instance (GHC.Internal.Show.Show b, GHC.Internal.Show.Show a) => GHC.Internal.Show.Show (Data.List.Trace.Trace a b)
+ Data.List.Trace: instance GHC.Internal.Base.Functor (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Alternative (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Applicative (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monad (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.MonadPlus (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Base.Monoid (Data.List.Trace.Trace a b)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fail.MonadFail (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Monoid a => GHC.Internal.Control.Monad.Fix.MonadFix (Data.List.Trace.Trace a)
+ Data.List.Trace: instance GHC.Internal.Base.Semigroup a => GHC.Internal.Base.Semigroup (Data.List.Trace.Trace a b)
+ Data.List.Trace: instance GHC.Internal.Show.Show a => Data.Functor.Classes.Show1 (Data.List.Trace.Trace a)
- Control.Monad.IOSim: ppTrace_ :: SimTrace a -> String
+ Control.Monad.IOSim: ppTrace_ :: Trace a SimEvent -> String
- Control.Monad.IOSim: selectTraceEvents' :: (Time -> SimEventType -> Maybe b) -> SimTrace a -> [b]
+ Control.Monad.IOSim: selectTraceEvents' :: (Time -> SimEventType -> Maybe b) -> Trace a SimEvent -> [b]
- Control.Monad.IOSim: selectTraceEventsDynamic' :: forall a b. Typeable b => SimTrace a -> [b]
+ Control.Monad.IOSim: selectTraceEventsDynamic' :: forall a b. Typeable b => Trace a SimEvent -> [b]
- Control.Monad.IOSim: selectTraceEventsDynamicWithTime' :: forall a b. Typeable b => SimTrace a -> [(Time, b)]
+ Control.Monad.IOSim: selectTraceEventsDynamicWithTime' :: forall a b. Typeable b => Trace a SimEvent -> [(Time, b)]
- Control.Monad.IOSim: selectTraceEventsSay' :: SimTrace a -> [String]
+ Control.Monad.IOSim: selectTraceEventsSay' :: Trace a SimEvent -> [String]
- Control.Monad.IOSim: selectTraceEventsSayWithTime' :: SimTrace a -> [(Time, String)]
+ Control.Monad.IOSim: selectTraceEventsSayWithTime' :: Trace a SimEvent -> [(Time, String)]

Files

CHANGELOG.md view
@@ -1,5 +1,40 @@ # Revision history of io-sim +## next release++### Breaking changes++### Non-breaking changes++## 1.8.0.0++- Provided `MonadTraceMVar`+- Renamed `InspectMonad` to `InspectMonadSTM`+- Support `threadLabel` (`io-classes-1.8`)+- `IOSimPOR`'s `Effect` traces now will correctly show labels on read/written+  `TVars`.+- `Show` instance for `ScheduleMod` now prints `ThreadId`s in a slightly nicer+  way, matching the way those steps would be traced in the `SimTrace`.+- Implement `MonadLabelledMVar` instance for `(IOSim s)`+- `TVarId` is now a sum type with one constructor per `TVar` role, e.g. `TVar`,+  `TMVar`, `MVar` and a few others - except for `TChan`.+- A blocked `takeTVar` is now safe in the presence of exceptions. It will relay+  the value to other waiting threads.+- Faster handling of timeouts and timers by using a more efficient+  internal representation.+- The signature of:+  - `selectTraceEvents'`,+  - `selectTraceEventsDynamic'`,+  - `selectTraceEventsDynamicWithTime'`,+  - `selectTraceEventsSay'` and+  - `selectTraceEventsSayWithTime'`+  is more general.  These functions now accepts trace with any result, rather+  than one that finishes with `SimResult`.+- More polymorphic `ppTrace_` type signature.+- Fixed `tryReadTBQueue` when returning `Nothing`.+- Support ghc 9.12+- Export `Time` from `Control.Monad.IOSim`.+ ## 1.6.0.0  - Upgraded to `io-classes-1.6.0.0`
README.md view
@@ -1,4 +1,4 @@-# [IO Simulator Monad][`io-sim`]: `io-sim`+# [IO Simulator Monad][`io-sim`]: `io-sim` package  A pure simulator monad built on top of the lazy `ST` monad which supports: @@ -40,8 +40,18 @@ also the other way around: that `GHC`s `STM` implementation meets the specification. +## Supporting material++* [Philipp Kant (@kantp) at Bobconf 2022][bob-conf]+* [Armando Santos (@bolt12) at ZuriHac 2022][zuriHac-2022]+* [Marcin Szamotulski (@coot) IOSim and Partial Order Reduction][io-sim-por-presentation]+ [`io-sim`]:  https://hackage.haskell.org/package/io-sim [`io-classes`]: https://input-output-hk.github.io/io-sim/io-classes/index.html [`si-timers`]: https://input-output-hk.github.io/io-sim/io-classes/si-timers/index.html [`IOSimPOR`]: https://github.com/input-output-hk/io-sim/tree/main/io-sim/how-to-use-IOSimPOR.md [`IOSim`]: https://hackage.haskell.org/package/io-sim/docs/Control-Monad-IOSim.html#t:IOSim++[bob-conf]: https://youtu.be/uedUGeWN4ZM+[zuriHac-2022]: https://youtu.be/tKIYQgJnGkA+[io-sim-por-presentation]: https://coot.me/presentations/iosimpor.pdf
bench/Main.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE ScopedTypeVariables #-}- module Main (main) where  import Control.Concurrent.Class.MonadSTM
io-sim.cabal view
@@ -1,21 +1,25 @@-cabal-version:       3.0+cabal-version:       3.4 name:                io-sim-version:             1.6.0.0+version:             1.8.0.0 synopsis:            A pure simulator for monadic concurrency with STM. description:-  A pure simulator monad with support of concurency (base & async style), stm,+  A pure simulator monad with support of concurrency (base & async style), stm,   synchronous and asynchronous exceptions, timeouts & delays, dynamic traces,-  partial order reduction and more.+  partial order reduction, and more.++  = Documentation+  Documentation is published+  [here](https://input-output-hk.github.io/io-sim/io-sim). license:             Apache-2.0 license-files:       LICENSE NOTICE-copyright:           2022-2024 Input Output Global Inc (IOG)+copyright:           2022-2025 Input Output Global Inc (IOG) author:              Alexander Vieth, Duncan Coutts, John Hughes, Marcin Szamotulski maintainer:          Duncan Coutts duncan@well-typed.com, Marcin Szamotulski coot@coot.me category:            Testing build-type:          Simple extra-doc-files:     CHANGELOG.md README.md bug-reports:         https://github.com/input-output-hk/io-sim/issues-tested-with:         GHC == { 8.10, 9.2, 9.4, 9.6, 9.8, 9.10 }+tested-with:         GHC == { 9.6, 9.8, 9.10, 9.12 }  flag asserts   description: Enable assertions@@ -56,31 +60,15 @@                        Control.Monad.IOSimPOR.QuickCheckUtils,                        Control.Monad.IOSimPOR.Timeout,                        Data.Deque.Strict-  default-language:    Haskell2010-  default-extensions:  ImportQualifiedPost-  other-extensions:    BangPatterns,-                       CPP,-                       DeriveFunctor,-                       DeriveGeneric,-                       DerivingVia,-                       ExistentialQuantification,-                       ExplicitNamespaces,-                       FlexibleContexts,-                       FlexibleInstances,-                       GADTSyntax,-                       GeneralizedNewtypeDeriving,-                       MultiParamTypeClasses,-                       NamedFieldPuns,-                       NumericUnderscores,-                       RankNTypes,-                       ScopedTypeVariables,-                       TypeFamilies-  build-depends:       base              >=4.9 && <4.21,+  default-language:    GHC2021+  default-extensions:  LambdaCase+  build-depends:       base              >=4.9 && <4.22,                        io-classes:{io-classes,strict-stm,si-timers}-                                        ^>=1.6,+                                        ^>=1.6 || ^>= 1.7 || ^>= 1.8,                        exceptions        >=0.10,                        containers,                        deepseq,+                       hashable,                        nothunks,                        primitive         >=0.7 && <0.11,                        psqueues          >=0.2 && <0.3,@@ -104,8 +92,8 @@                        Test.Control.Monad.Utils                        Test.Control.Monad.IOSim                        Test.Control.Monad.IOSimPOR-  default-language:    Haskell2010-  default-extensions:  ImportQualifiedPost+  default-language:    GHC2021+  default-extensions:  LambdaCase   build-depends:       base,                        array,                        containers,@@ -126,8 +114,8 @@   type:                exitcode-stdio-1.0   hs-source-dirs:      bench   main-is:             Main.hs-  default-language:    Haskell2010-  default-extensions:  ImportQualifiedPost+  default-language:    GHC2021+  default-extensions:  LambdaCase   build-depends:       base,                        criterion ^>= 1.6, 
src/Control/Monad/IOSim.hs view
@@ -1,11 +1,7 @@ {-# LANGUAGE ExplicitNamespaces    #-}-{-# LANGUAGE NamedFieldPuns        #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE TupleSections         #-}+{-# LANGUAGE QuantifiedConstraints #-}  {-# OPTIONS_GHC -Wno-name-shadowing #-}-{-# LANGUAGE QuantifiedConstraints #-} module Control.Monad.IOSim   ( -- * Simulation monad     IOSim@@ -42,6 +38,7 @@   , unshareClock     -- * Simulation trace   , type SimTrace+  , Time (..)   , Trace (Cons, Nil, SimTrace, SimPORTrace, TraceDeadlock, TraceLoop, TraceMainReturn, TraceMainException, TraceRacesFound, TraceInternalError)   , traceEvents   , traceResult@@ -144,11 +141,12 @@     . traceSelectTraceEvents fn  -- | Like 'selectTraceEvents', but it returns even if the simulation trace ends--- with 'Failure'.+-- with 'Failure'.  It also works with any return type, not only `SimResult`+-- like `selectTraceEvents` does. -- selectTraceEvents'     :: (Time ->  SimEventType -> Maybe b)-    -> SimTrace a+    -> Trace a SimEvent     -> [b] selectTraceEvents' fn =       bifoldr ( \ _ _   -> []  )@@ -225,20 +223,22 @@     fn t (EventLog dyn) = (t,) <$> fromDynamic dyn     fn _ _              = Nothing --- | Like 'selectTraceEventsDynamic' but it returns even if the simulation trace--- ends with 'Failure'.+-- | Like 'selectTraceEventsDynamic' but it returns even if the simulation+-- trace ends with 'Failure'.  It also works with any return type, not only+-- `SimResult` like `selectTraceEventsDynamic` does. ---selectTraceEventsDynamic' :: forall a b. Typeable b => SimTrace a -> [b]+selectTraceEventsDynamic' :: forall a b. Typeable b => Trace a SimEvent -> [b] selectTraceEventsDynamic' = selectTraceEvents' fn   where     fn :: Time -> SimEventType -> Maybe b     fn _ (EventLog dyn) = fromDynamic dyn     fn _ _              = Nothing --- | Like `selectTraceEventsDynamic'` but it also captures time of the trace--- event.+-- | Like `selectTraceEventsDynamicWithTime'` but it also captures time of the+-- trace event.  It also works with any return type, not only `SimResult` like+-- `selectTraceEventsDynamicWithTime` does. ---selectTraceEventsDynamicWithTime' :: forall a b. Typeable b => SimTrace a -> [(Time, b)]+selectTraceEventsDynamicWithTime' :: forall a b. Typeable b => Trace a SimEvent -> [(Time, b)] selectTraceEventsDynamicWithTime' = selectTraceEvents' fn   where     fn :: Time -> SimEventType -> Maybe (Time, b)@@ -266,9 +266,10 @@     fn _ _            = Nothing  -- | Like 'selectTraceEventsSay' but it returns even if the simulation trace--- ends with 'Failure'.+-- ends with 'Failure'.  It also works with any return type, not only `SimResult`+-- like `selectTraceEventsSay` does. ---selectTraceEventsSay' :: SimTrace a -> [String]+selectTraceEventsSay' :: Trace a SimEvent -> [String] selectTraceEventsSay' = selectTraceEvents' fn   where     fn :: Time -> SimEventType -> Maybe String@@ -277,7 +278,7 @@  -- | Like `selectTraceEventsSay'` but it also captures time of the trace event. ---selectTraceEventsSayWithTime' :: SimTrace a -> [(Time, String)]+selectTraceEventsSayWithTime' :: Trace a SimEvent -> [(Time, String)] selectTraceEventsSayWithTime' = selectTraceEvents' fn   where     fn :: Time -> SimEventType -> Maybe (Time, String)
src/Control/Monad/IOSim/CommonTypes.hs view
@@ -1,10 +1,6 @@-{-# LANGUAGE DeriveAnyClass             #-}-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE DerivingStrategies         #-}-{-# LANGUAGE DerivingVia                #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia        #-}  -- | Common types shared between `IOSim` and `IOSimPOR`. --@@ -16,6 +12,7 @@   , childThreadId   , setRacyThread   , TVarId (..)+  , VarId   , TimeoutId (..)   , ClockId (..)   , VectorClock (..)@@ -25,9 +22,12 @@   , TVarLabel   , TVar (..)   , SomeTVar (..)+  , someTVarToLabelled   , Deschedule (..)   , ThreadStatus (..)   , BlockedReason (..)+  , Labelled (..)+  , ppLabelled     -- * Utils   , ppList   ) where@@ -38,6 +38,7 @@  import NoThunks.Class +import Data.Hashable import Data.List (intercalate, intersperse) import Data.Map (Map) import Data.Map qualified as Map@@ -65,6 +66,8 @@   deriving anyclass NFData   deriving anyclass NoThunks +instance Hashable IOSimThreadId+ ppIOSimThreadId :: IOSimThreadId -> String ppIOSimThreadId (RacyThreadId as) = "Thread {"++ intercalate "," (map show as) ++"}" ppIOSimThreadId     (ThreadId as) = "Thread " ++ show as@@ -88,7 +91,24 @@ ppStepId (tid, step) = concat [ppIOSimThreadId tid, ".", show step]  -newtype TVarId      = TVarId    Int   deriving (Eq, Ord, Enum, Show)+type VarId = Int+-- | 'TVar's are used to emulate other shared variables. Each one comes with+-- its own id constructor.+data TVarId =+    TVarId  !VarId+    -- ^ a `TVar`+  | TMVarId !VarId+    -- ^ a `TMVar` simulated by a `TVar`.+  | MVarId  !VarId+    -- ^ an `MVar` simulated by a `TVar`.+  | TQueueId !VarId+    -- ^ a 'TQueue` simulated by a `TVar`.+  | TBQueueId !VarId+    -- ^ a 'TBQueue` simulated by a `TVar`.+  | TSemId !VarId+    -- ^ a 'TSem` simulated by a `TVar`.+  -- TODO: `TChan`+  deriving (Eq, Ord, Show) newtype TimeoutId   = TimeoutId Int   deriving (Eq, Ord, Enum, Show) newtype ClockId     = ClockId   [Int] deriving (Eq, Ord, Show) newtype VectorClock = VectorClock { getVectorClock :: Map IOSimThreadId Int }@@ -135,7 +155,7 @@        tvarVClock  :: !(STRef s VectorClock),         -- | Callback to construct a trace which will be attached to the dynamic-       -- trace.+       -- trace each time the `TVar` is committed.        tvarTrace   :: !(STRef s (Maybe (Maybe a -> a -> ST s TraceValue)))      } @@ -145,6 +165,11 @@ data SomeTVar s where   SomeTVar :: !(TVar s a) -> SomeTVar s +someTVarToLabelled :: SomeTVar s -> ST s (Labelled (SomeTVar s))+someTVarToLabelled tv@(SomeTVar var) = do+  lbl <- readSTRef (tvarLabel var)+  pure (Labelled tv lbl)+ data Deschedule = Yield                 | Interruptable                 | Blocked BlockedReason@@ -161,6 +186,21 @@                    | BlockedOnDelay                    | BlockedOnThrowTo   deriving (Eq, Show)++-- | A labelled value.+--+-- For example 'labelThread' or `labelTVar' will insert a label to `IOSimThreadId`+-- (or `TVarId`).+data Labelled a = Labelled {+    l_labelled :: !a,+    l_label    :: !(Maybe String)+  }+  deriving (Eq, Ord, Generic, Functor)+  deriving Show via Quiet (Labelled a)++ppLabelled :: (a -> String) -> Labelled a -> String+ppLabelled pp Labelled { l_labelled = a, l_label = Nothing  } = pp a+ppLabelled pp Labelled { l_labelled = a, l_label = Just lbl } = concat ["Labelled ", pp a, " ", lbl]  -- -- Utils
src/Control/Monad/IOSim/Internal.hs view
@@ -1,14 +1,6 @@-{-# LANGUAGE BangPatterns              #-}-{-# LANGUAGE CPP                       #-}-{-# LANGUAGE DerivingVia               #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleInstances         #-}-{-# LANGUAGE GADTSyntax                #-}-{-# LANGUAGE MultiParamTypeClasses     #-}-{-# LANGUAGE NamedFieldPuns            #-}-{-# LANGUAGE RankNTypes                #-}-{-# LANGUAGE ScopedTypeVariables       #-}-{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE CPP          #-}+{-# LANGUAGE DerivingVia  #-}+{-# LANGUAGE TypeFamilies #-}  -- incomplete uni patterns in 'schedule' (when interpreting 'StmTxCommitted') -- and 'reschedule'.@@ -48,17 +40,18 @@  import Prelude hiding (read) +import Data.Coerce import Data.Deque.Strict (Deque) import Data.Deque.Strict qualified as Deque import Data.Dynamic import Data.Foldable (foldlM, toList, traverse_)+import Data.IntPSQ (IntPSQ)+import Data.IntPSQ qualified as PSQ import Data.List qualified as List import Data.List.Trace qualified as Trace import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Maybe (mapMaybe)-import Data.OrdPSQ (OrdPSQ)-import Data.OrdPSQ qualified as PSQ import Data.Set (Set) import Data.Set qualified as Set import Data.Time (UTCTime (..), fromGregorian)@@ -134,7 +127,7 @@      -- `TimeoutId` (only used to report in a trace).  -type Timeouts s = OrdPSQ TimeoutId Time (TimerCompletionInfo s)+type Timeouts s = IntPSQ Time (TimerCompletionInfo s)  -- | Internal state. --@@ -149,7 +142,7 @@        timers   :: !(Timeouts s),        -- | list of clocks        clocks   :: !(Map ClockId UTCTime),-       nextVid  :: !TVarId,     -- ^ next unused 'TVarId'+       nextVid  :: !VarId,     -- ^ next unused 'VarId'        nextTmid :: !TimeoutId   -- ^ next unused 'TimeoutId'      } @@ -161,7 +154,7 @@       curTime  = Time 0,       timers   = PSQ.empty,       clocks   = Map.singleton (ClockId []) epoch1970,-      nextVid  = TVarId 0,+      nextVid  = 0,       nextTmid = TimeoutId 0     }   where@@ -263,7 +256,7 @@        DelayFrame tmid k ctl' -> do         let thread' = thread { threadControl = ThreadControl k ctl' }-            timers' = PSQ.delete tmid timers+            timers' = (PSQ.delete . coerce) tmid timers         schedule thread' simstate { timers = timers' }      Throw e -> case unwindControlStack e thread timers of@@ -358,9 +351,9 @@       error "schedule: StartTimeout: Impossible happened"      StartTimeout d action' k -> do-      !lock <- TMVar <$> execNewTVar nextVid (Just $! "lock-" ++ show nextTmid) Nothing+      !lock <- TMVar <$> execNewTVar (TMVarId nextVid) (Just $! "lock-" ++ show nextTmid) Nothing       let !expiry    = d `addTime` time-          !timers'   = PSQ.insert nextTmid expiry (TimerTimeout tid nextTmid lock) timers+          !timers'   = (PSQ.insert . coerce) nextTmid expiry (TimerTimeout tid nextTmid lock) timers           !thread'   = thread { threadControl =                                  ThreadControl action'                                                (TimeoutFrame nextTmid lock k ctl)@@ -373,31 +366,31 @@      UnregisterTimeout tmid k -> do       let thread' = thread { threadControl = ThreadControl k ctl }-      schedule thread' simstate { timers = PSQ.delete tmid timers }+      schedule thread' simstate { timers = (PSQ.delete . coerce) tmid timers }      RegisterDelay d k | d < 0 -> do-      !tvar <- execNewTVar nextVid+      !tvar <- execNewTVar (TVarId nextVid)                           (Just $! "<<timeout " ++ show (unTimeoutId nextTmid) ++ ">>")                           True       let !expiry  = d `addTime` time           !thread' = thread { threadControl = ThreadControl (k tvar) ctl }       trace <- schedule thread' simstate { nextVid = succ nextVid }-      return (SimTrace time tid tlbl (EventRegisterDelayCreated nextTmid nextVid expiry) $+      return (SimTrace time tid tlbl (EventRegisterDelayCreated nextTmid (TVarId nextVid) expiry) $               SimTrace time tid tlbl (EventRegisterDelayFired nextTmid) $               trace)      RegisterDelay d k -> do-      !tvar <- execNewTVar nextVid+      !tvar <- execNewTVar (TVarId nextVid)                           (Just $! "<<timeout " ++ show (unTimeoutId nextTmid) ++ ">>")                           False       let !expiry  = d `addTime` time-          !timers' = PSQ.insert nextTmid expiry (TimerRegisterDelay tvar) timers+          !timers' = (PSQ.insert . coerce) nextTmid expiry (TimerRegisterDelay tvar) timers           !thread' = thread { threadControl = ThreadControl (k tvar) ctl }       trace <- schedule thread' simstate { timers   = timers'                                          , nextVid  = succ nextVid                                          , nextTmid = succ nextTmid }       return (SimTrace time tid tlbl-                (EventRegisterDelayCreated nextTmid nextVid expiry) trace)+                (EventRegisterDelayCreated nextTmid (TVarId nextVid) expiry) trace)      ThreadDelay d k | d < 0 -> do       let !expiry    = d `addTime` time@@ -410,7 +403,7 @@      ThreadDelay d k -> do       let !expiry  = d `addTime` time-          !timers' = PSQ.insert nextTmid expiry (TimerThreadDelay tid nextTmid) timers+          !timers' = (PSQ.insert . coerce) nextTmid expiry (TimerThreadDelay tid nextTmid) timers           !thread' = thread { threadControl = ThreadControl (Return ()) (DelayFrame nextTmid k ctl) }       !trace <- deschedule (Blocked BlockedOnDelay) thread' simstate { timers   = timers'                                                                      , nextTmid = succ nextTmid }@@ -424,25 +417,25 @@           !expiry  = d `addTime` time           !thread' = thread { threadControl = ThreadControl (k t) ctl }       trace <- schedule thread' simstate { nextTmid = succ nextTmid }-      return (SimTrace time tid tlbl (EventTimerCreated nextTmid nextVid expiry) $+      return (SimTrace time tid tlbl (EventTimerCreated nextTmid (TVarId nextVid) expiry) $               SimTrace time tid tlbl (EventTimerCancelled nextTmid) $               trace)      NewTimeout d k -> do-      !tvar  <- execNewTVar nextVid+      !tvar  <- execNewTVar (TVarId nextVid)                            (Just $! "<<timeout-state " ++ show (unTimeoutId nextTmid) ++ ">>")                            TimeoutPending       let !expiry  = d `addTime` time           !t       = Timeout tvar nextTmid-          !timers' = PSQ.insert nextTmid expiry (Timer tvar) timers+          !timers' = (PSQ.insert . coerce) nextTmid expiry (Timer tvar) timers           !thread' = thread { threadControl = ThreadControl (k t) ctl }       trace <- schedule thread' simstate { timers   = timers'                                          , nextVid  = succ nextVid                                          , nextTmid = succ nextTmid }-      return (SimTrace time tid tlbl (EventTimerCreated nextTmid nextVid expiry) trace)+      return (SimTrace time tid tlbl (EventTimerCreated nextTmid (TVarId nextVid) expiry) trace)      CancelTimeout (Timeout tvar tmid) k -> do-      let !timers' = PSQ.delete tmid timers+      let !timers' = (PSQ.delete . coerce) tmid timers           !thread' = thread { threadControl = ThreadControl k ctl }       !written <- execAtomically' (runSTM $ writeTVar tvar TimeoutCancelled)       -- note: we are not running traceTVar on 'tvar', since its not exposed to@@ -547,6 +540,13 @@                            , threadLabel   = Just l }       schedule thread' simstate +    GetThreadLabel tid' k -> do+      let tlbl' | tid' == tid = tlbl+                | otherwise   = tid' `Map.lookup` threads+                            >>= threadLabel+          thread' = thread { threadControl = ThreadControl (k tlbl') ctl }+      schedule thread' simstate+     LabelThread tid' l k -> do       let thread'  = thread { threadControl = ThreadControl k ctl }           threads' = Map.adjust (\t -> t { threadLabel = Just l }) tid' threads@@ -918,8 +918,8 @@   where     unwind :: forall s' c. MaskingState            -> ControlStack s' c a-           -> OrdPSQ TimeoutId Time (TimerCompletionInfo s)-           -> (Either Bool (Thread s' a), OrdPSQ TimeoutId Time (TimerCompletionInfo s))+           -> IntPSQ Time (TimerCompletionInfo s)+           -> (Either Bool (Thread s' a), IntPSQ Time (TimerCompletionInfo s))     unwind _  MainFrame                 timers = (Left True, timers)     unwind _  ForkFrame                 timers = (Left False, timers)     unwind _ (MaskFrame _k maskst' ctl) timers = unwind maskst' ctl timers@@ -955,13 +955,13 @@           _ -> unwind maskst ctl timers'       where         -- Remove the timeout associated with the 'TimeoutFrame'.-        timers' = PSQ.delete tmid timers+        timers' = (PSQ.delete . coerce) tmid timers      unwind maskst (DelayFrame tmid _k ctl) timers =         unwind maskst ctl timers'       where         -- Remove the timeout associated with the 'DelayFrame'.-        timers' = PSQ.delete tmid timers+        timers' = (PSQ.delete . coerce) tmid timers       atLeastInterruptibleMask :: MaskingState -> MaskingState@@ -969,10 +969,10 @@     atLeastInterruptibleMask ms       = ms  -removeMinimums :: (Ord k, Ord p)-               => OrdPSQ k p a-               -> Maybe ([k], p, [a], OrdPSQ k p a)-removeMinimums = \psq ->+removeMinimums :: (Coercible k Int, Ord p)+               => IntPSQ p a+               -> Maybe ([k], p, [a], IntPSQ p a)+removeMinimums = \psq -> coerce $     case PSQ.minView psq of       Nothing              -> Nothing       Just (k, p, x, psq') -> Just (collectAll [k] p [x] psq')@@ -1023,7 +1023,7 @@                   Time                -> IOSimThreadId                -> Maybe ThreadLabel-               -> TVarId+               -> VarId                -> StmA s a                -> (StmTxResult s a -> ST s (SimTrace c))                -> ST s (SimTrace c)@@ -1036,7 +1036,7 @@        -> Map TVarId (SomeTVar s)  -- set of vars written        -> [SomeTVar s]             -- vars written in order (no dups)        -> [SomeTVar s]             -- vars created in order-       -> TVarId                   -- var fresh name supply+       -> VarId                   -- var fresh name supply        -> StmA s b        -> ST s (SimTrace c)     go !ctl !read !written !writtenSeq !createdSeq !nextVid !action =@@ -1138,8 +1138,8 @@         let ctl' = BranchFrame (OrElseStmA b) k written writtenSeq createdSeq ctl         go ctl' read Map.empty [] [] nextVid a -      NewTVar !mbLabel x k -> do-        !v <- execNewTVar nextVid mbLabel x+      NewTVar mkId !mbLabel x k -> do+        !v <- execNewTVar (mkId nextVid) mbLabel x         go ctl read written writtenSeq (SomeTVar v : createdSeq) (succ nextVid) (k v)        LabelTVar !label tvar k -> do@@ -1222,16 +1222,15 @@   execNewTVar :: TVarId -> Maybe String -> a -> ST s (TVar s a)-execNewTVar nextVid !mbLabel x = do+execNewTVar !tvarId !mbLabel x = do     !tvarLabel   <- newSTRef mbLabel     !tvarCurrent <- newSTRef x     !tvarUndo    <- newSTRef $! []     !tvarBlocked <- newSTRef ([], Set.empty)     !tvarVClock  <- newSTRef $! VectorClock Map.empty     !tvarTrace   <- newSTRef $! Nothing-    return TVar {tvarId = nextVid, tvarLabel,-                 tvarCurrent, tvarUndo, tvarBlocked, tvarVClock,-                 tvarTrace}+    return TVar {tvarId, tvarLabel, tvarCurrent, tvarUndo, tvarBlocked,+                 tvarVClock, tvarTrace}   -- 'execReadTVar' is defined in `Control.Monad.IOSim.Type` and shared with /IOSimPOR/
src/Control/Monad/IOSim/InternalTypes.hs view
@@ -1,6 +1,4 @@-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}  -- | Internal types shared between `IOSim` and `IOSimPOR`. --
src/Control/Monad/IOSim/STM.hs view
@@ -1,7 +1,4 @@-{-# LANGUAGE BangPatterns          #-} {-# LANGUAGE QuantifiedConstraints #-}-{-# LANGUAGE TypeFamilies          #-}-{-# LANGUAGE TypeOperators         #-}  -- | 'io-sim' implementation of 'TQueue', 'TBQueue' and 'MVar'. --@@ -38,7 +35,7 @@   :: MonadTraceSTM m   => proxy m   -> TQueueDefault m a-  -> (Maybe [a] -> [a] -> InspectMonad m TraceValue)+  -> (Maybe [a] -> [a] -> InspectMonadSTM m TraceValue)   -> STM m () traceTQueueDefault p (TQueue queue) f =     traceTVar p queue@@ -122,7 +119,7 @@   :: MonadTraceSTM m   => proxy m   -> TBQueueDefault m a-  -> (Maybe [a] -> [a] -> InspectMonad m TraceValue)+  -> (Maybe [a] -> [a] -> InspectMonadSTM m TraceValue)   -> STM m () traceTBQueueDefault p (TBQueue queue _size) f =     traceTVar p queue (\mas as -> f (g <$> mas) (g as))@@ -149,9 +146,7 @@       return (Just x)     [] ->       case reverse ys of-        [] -> do-          writeTVar queue $! (xs, r', ys, w)-          return Nothing+        [] -> return Nothing          -- NB. lazy: we want the transaction to be         -- short, otherwise it will conflict@@ -234,7 +229,7 @@ -- -- /Implementation details:/ ----- 'STM' does not guarantee fairness, instead it provide compositionally.+-- @STM@ does not guarantee fairness, instead it provides compositionally. -- Fairness of 'putMVarDefault' and 'takeMVarDefault' is provided by tracking -- queue of blocked operation in the 'MVarState', e.g.  when a 'putMVarDefault' -- is scheduled on a full 'MVar', the request is put on to the back of the queue@@ -261,13 +256,17 @@ newEmptyMVarDefault :: MonadSTM m => m (MVarDefault m a) newEmptyMVarDefault = MVar <$> newTVarIO (MVarEmpty mempty mempty) +labelMVarDefault+  :: MonadLabelledSTM m+  => MVarDefault m a -> String -> m ()+labelMVarDefault (MVar tvar) = atomically . labelTVar tvar  newMVarDefault :: MonadSTM m => a -> m (MVarDefault m a) newMVarDefault a = MVar <$> newTVarIO (MVarFull a mempty)   putMVarDefault :: ( MonadMask  m-                  , MonadSTM   m+                  , MonadLabelledSTM m                   , forall x tvar. tvar ~ TVar m x => Eq tvar                   )                => MVarDefault m a -> a -> m ()@@ -278,6 +277,7 @@         -- It's full, add ourselves to the end of the 'put' blocked queue.         MVarFull x' putq -> do           putvar <- newTVar False+          labelTVar putvar "internal-putvar"           writeTVar tv (MVarFull x' (Deque.snoc (x, putvar) putq))           return (Just putvar) @@ -350,7 +350,7 @@   takeMVarDefault :: ( MonadMask m-                   , MonadSTM  m+                   , MonadLabelledSTM m                    , forall x tvar. tvar ~ TVar m x => Eq tvar                    )                 => MVarDefault m a@@ -362,6 +362,7 @@         -- It's empty, add ourselves to the end of the 'take' blocked queue.         MVarEmpty takeq readq -> do           takevar <- newTVar Nothing+          labelTVar takevar "internal-takevar"           writeTVar tv (MVarEmpty (Deque.snoc takevar takeq) readq)           return (Left takevar) @@ -392,8 +393,25 @@                 -- takevar; we need to remove it from 'takeq', otherwise we                 -- will have a space leak.                 let takeq' = Deque.filter (/= takevar) takeq-                writeTVar tv (MVarEmpty takeq' readq)+                takevalue <- readTVar takevar+                case takevalue of+                  Nothing ->+                    writeTVar tv (MVarEmpty takeq' readq)+                  -- we were given a value before we could read it. Relay it to any+                  -- new reading threads and possible the next take thread.+                  Just x -> do+                    -- notify readers+                    mapM_ (\readvar -> writeTVar readvar (Just x)) readq +                    -- notify first `takeMVar` thread+                    case Deque.uncons takeq' of+                      Nothing ->+                        writeTVar tv (MVarFull x mempty)++                      Just (takevar', takeq'') -> do+                        writeTVar takevar' (Just x)+                        writeTVar tv (MVarEmpty takeq'' mempty)+               -- This case is unlikely but possible if another thread ran               -- first and modified the mvar. This situation is fine as far as               -- space leaks are concerned because it means our wait var is no@@ -433,7 +451,7 @@ -- 'putMVar' value.  It will also not block if the 'MVar' is full, even if there -- are other threads attempting to 'putMVar'. ---readMVarDefault :: ( MonadSTM m+readMVarDefault :: ( MonadLabelledSTM m                    , MonadMask m                    , forall x tvar. tvar ~ TVar m x => Eq tvar                    )@@ -446,6 +464,7 @@         -- It's empty, add ourselves to the 'read' blocked queue.         MVarEmpty takeq readq -> do           readvar <- newTVar Nothing+          labelTVar readvar "internal-readvar"           writeTVar tv (MVarEmpty takeq (Deque.snoc readvar readq))           return (Left readvar) 
src/Control/Monad/IOSim/Types.hs view
@@ -1,22 +1,13 @@-{-# LANGUAGE BangPatterns              #-}-{-# LANGUAGE CPP                       #-}-{-# LANGUAGE DeriveFunctor             #-}-{-# LANGUAGE DeriveGeneric             #-}-{-# LANGUAGE DerivingVia               #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleInstances         #-}-{-# LANGUAGE GADTSyntax                #-}-{-# LANGUAGE LambdaCase                #-}-{-# LANGUAGE MultiParamTypeClasses     #-}-{-# LANGUAGE NamedFieldPuns            #-}-{-# LANGUAGE NumericUnderscores        #-}-{-# LANGUAGE PatternSynonyms           #-}-{-# LANGUAGE RankNTypes                #-}-{-# LANGUAGE ScopedTypeVariables       #-}-{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE DerivingVia     #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TypeFamilies    #-}  -- Needed for `SimEvent` type. {-# OPTIONS_GHC -Wno-partial-fields    #-}+-- `MonadMaskingState` is deprecated in `io-classes`, but we provide an instance+-- for it.+{-# OPTIONS_GHC -Wno-deprecations      #-}  module Control.Monad.IOSim.Types   ( IOSim (..)@@ -58,12 +49,11 @@   , ppTrace_   , ppSimEvent   , ppDebug-  , Labelled (..)   , module Control.Monad.IOSim.CommonTypes   , Thrower (..)-  , Time (..)-  , addTime-  , diffTime+  , SI.Time (..)+  , SI.addTime+  , SI.diffTime     -- * Internal API   , Timeout (..)   , newTimeout@@ -91,14 +81,15 @@ import Control.Monad.Class.MonadST import Control.Monad.Class.MonadSTM.Internal (MonadInspectSTM (..),            MonadLabelledSTM (..), MonadSTM, MonadTraceSTM (..), TArrayDefault,-           TChanDefault, TMVarDefault, TSemDefault, TraceValue, atomically,-           retry)+           TChanDefault (..), TMVarDefault (..), TSemDefault (..), TraceValue,+           atomically, retry) import Control.Monad.Class.MonadSTM.Internal qualified as MonadSTM import Control.Monad.Class.MonadTest import Control.Monad.Class.MonadThrow as MonadThrow hiding (getMaskingState) import Control.Monad.Class.MonadThrow qualified as MonadThrow import Control.Monad.Class.MonadTime-import Control.Monad.Class.MonadTime.SI+import Control.Monad.Class.MonadTime.SI (DiffTime)+import Control.Monad.Class.MonadTime.SI qualified as SI import Control.Monad.Class.MonadTimer import Control.Monad.Class.MonadTimer.SI (TimeoutState (..)) import Control.Monad.Class.MonadTimer.SI qualified as SI@@ -168,7 +159,7 @@    LiftST       :: StrictST.ST s a -> (a -> SimA s b) -> SimA s b -  GetMonoTime  :: (Time    -> SimA s b) -> SimA s b+  GetMonoTime  :: (SI.Time    -> SimA s b) -> SimA s b   GetWallTime  :: (UTCTime -> SimA s b) -> SimA s b   SetWallTime  ::  UTCTime -> SimA s b  -> SimA s b   UnshareClock :: SimA s b -> SimA s b@@ -186,9 +177,10 @@                   SimA s a -> (e -> SimA s a) -> (a -> SimA s b) -> SimA s b   Evaluate     :: a -> (a -> SimA s b) -> SimA s b -  Fork         :: IOSim s () -> (IOSimThreadId -> SimA s b) -> SimA s b-  GetThreadId  :: (IOSimThreadId -> SimA s b) -> SimA s b-  LabelThread  :: IOSimThreadId -> String -> SimA s b -> SimA s b+  Fork           :: IOSim s () -> (IOSimThreadId -> SimA s b) -> SimA s b+  GetThreadId    :: (IOSimThreadId -> SimA s b) -> SimA s b+  LabelThread    :: IOSimThreadId -> String -> SimA s b -> SimA s b+  GetThreadLabel :: IOSimThreadId -> (Maybe String -> SimA s b) -> SimA s b    Atomically   :: STM  s a -> (a -> SimA s b) -> SimA s b @@ -219,7 +211,7 @@   ThrowStm     :: SomeException -> StmA s a   CatchStm     :: StmA s a -> (SomeException -> StmA s a) -> (a -> StmA s b) -> StmA s b -  NewTVar      :: Maybe String -> x -> (TVar s x -> StmA s b) -> StmA s b+  NewTVar      :: (VarId -> TVarId) -> Maybe String -> x -> (TVar s x -> StmA s b) -> StmA s b   LabelTVar    :: String -> TVar s a -> StmA s b -> StmA s b   ReadTVar     :: TVar s a -> (a -> StmA s b) -> StmA s b   WriteTVar    :: TVar s a ->  a -> StmA s b  -> StmA s b@@ -236,8 +228,8 @@   LiftSTStm    :: StrictST.ST s a -> (a -> StmA s b) -> StmA s b   FixStm       :: (x -> STM s x) -> (x -> StmA s r) -> StmA s r --- | `IOSim`'s 'MonadSTM.STM' monad, as 'IOSim' it is parametrised by @s@, e.g.--- @STMSim s a@ is monadic expression of type @a@.+-- | `IOSim`'s 'Control.Monad.Class.MonadSTM.Internal.STM' monad, as 'IOSim' it+-- is parametrised by @s@, e.g. @STMSim s a@ is monadic expression of type @a@. -- type STMSim = STM @@ -424,7 +416,6 @@         MaskedInterruptible   -> blockUninterruptible $ action block         MaskedUninterruptible -> action blockUninterruptible -instance MonadMaskingState (IOSim s) where   getMaskingState = getMaskingStateImpl   interruptible action = do       b <- getMaskingStateImpl@@ -433,6 +424,8 @@         MaskedInterruptible   -> unblock action         MaskedUninterruptible -> action +instance MonadMaskingState (IOSim s)+ instance Exceptions.MonadMask (IOSim s) where   mask                = MonadThrow.mask   uninterruptibleMask = MonadThrow.uninterruptibleMask@@ -469,6 +462,7 @@   type ThreadId (IOSim s) = IOSimThreadId   myThreadId       = IOSim $ oneShot $ \k -> GetThreadId k   labelThread t l  = IOSim $ oneShot $ \k -> LabelThread t l (k ())+  threadLabel t    = IOSim $ oneShot $ \k -> GetThreadLabel t k  instance MonadFork (IOSim s) where   forkIO task        = IOSim $ oneShot $ \k -> Fork task k@@ -478,6 +472,7 @@                        forkIO $ try (restore task) >>= k   throwTo tid e      = IOSim $ oneShot $ \k -> ThrowTo (toException e) tid (k ())   yield              = IOSim $ oneShot $ \k -> YieldSim (k ())+  getNumCapabilities = return 1  instance MonadTest (IOSim s) where   exploreRaces       = IOSim $ oneShot $ \k -> ExploreRaces (k ())@@ -507,14 +502,14 @@    atomically action = IOSim $ oneShot $ \k -> Atomically action k -  newTVar         x = STM $ oneShot $ \k -> NewTVar Nothing x k+  newTVar         x = STM $ oneShot $ \k -> NewTVar TVarId Nothing x k   readTVar   tvar   = STM $ oneShot $ \k -> ReadTVar tvar k   writeTVar  tvar x = STM $ oneShot $ \k -> WriteTVar tvar x (k ())   retry             = STM $ oneShot $ \_ -> Retry   orElse        a b = STM $ oneShot $ \k -> OrElse (runSTM a) (runSTM b) k -  newTMVar          = MonadSTM.newTMVarDefault-  newEmptyTMVar     = MonadSTM.newEmptyTMVarDefault+  newTMVar          = \a -> STM $ oneShot $ \k -> NewTVar TMVarId Nothing (Just a) (k . TMVar)+  newEmptyTMVar     = STM $ oneShot $ \k -> NewTVar TMVarId Nothing Nothing (k . TMVar)   takeTMVar         = MonadSTM.takeTMVarDefault   tryTakeTMVar      = MonadSTM.tryTakeTMVarDefault   putTMVar          = MonadSTM.putTMVarDefault@@ -525,7 +520,7 @@   writeTMVar        = MonadSTM.writeTMVarDefault   isEmptyTMVar      = MonadSTM.isEmptyTMVarDefault -  newTQueue         = newTQueueDefault+  newTQueue         = STM $ oneShot $ \k -> NewTVar TQueueId Nothing ([], []) (k . TQueue)   readTQueue        = readTQueueDefault   tryReadTQueue     = tryReadTQueueDefault   peekTQueue        = peekTQueueDefault@@ -535,7 +530,10 @@   isEmptyTQueue     = isEmptyTQueueDefault   unGetTQueue       = unGetTQueueDefault -  newTBQueue        = newTBQueueDefault+  newTBQueue size   | size >= fromIntegral (maxBound :: Int)+                    = error "newTBQueue: size larger than Int"+                    | otherwise+                    = STM $ oneShot $ \k -> NewTVar TBQueueId Nothing ([], 0, [], size) (k . (`TBQueue` size ))   readTBQueue       = readTBQueueDefault   tryReadTBQueue    = tryReadTBQueueDefault   peekTBQueue       = peekTBQueueDefault@@ -547,7 +545,7 @@   isFullTBQueue     = isFullTBQueueDefault   unGetTBQueue      = unGetTBQueueDefault -  newTSem           = MonadSTM.newTSemDefault+  newTSem           = \i -> STM $ oneShot $ \k -> NewTVar TSemId Nothing i (k . TSem)   waitTSem          = MonadSTM.waitTSemDefault   signalTSem        = MonadSTM.signalTSemDefault   signalTSemN       = MonadSTM.signalTSemNDefault@@ -565,7 +563,7 @@   cloneTChan        = MonadSTM.cloneTChanDefault  instance MonadInspectSTM (IOSim s) where-  type InspectMonad (IOSim s) = ST s+  type InspectMonadSTM (IOSim s) = ST s   inspectTVar  _                 TVar { tvarCurrent }  = readSTRef tvarCurrent   inspectTMVar _ (MonadSTM.TMVar TVar { tvarCurrent }) = readSTRef tvarCurrent @@ -587,8 +585,8 @@  instance MonadMVar (IOSim s) where   type MVar (IOSim s) = MVarDefault (IOSim s)-  newEmptyMVar = newEmptyMVarDefault-  newMVar      = newMVarDefault+  newEmptyMVar = atomically $ STM $ oneShot $ \k -> NewTVar MVarId Nothing (MVarEmpty mempty mempty) (k . MVar)+  newMVar      = \a -> atomically $ STM $ oneShot $ \k -> NewTVar MVarId Nothing (MVarFull a mempty) (k . MVar)   takeMVar     = takeMVarDefault   putMVar      = putMVarDefault   tryTakeMVar  = tryTakeMVarDefault@@ -605,6 +603,29 @@         MVarEmpty _ _ -> pure Nothing         MVarFull x _  -> pure (Just x) +instance MonadTraceMVar (IOSim s) where+  traceMVarIO :: forall proxy a.+                 proxy+              -> MVar (IOSim s) a+              -> (    Maybe (Maybe a)+                  ->         Maybe a+                  -> ST s TraceValue+                 )+              -> IOSim s ()+  traceMVarIO _ (MVar mvar) f = traceTVarIO mvar f'+    where+      f' :: Maybe (MVarState m a)+         ->        MVarState m a+         -> ST s TraceValue+      f' mst st = f (g <$> mst) (g st)++      g :: MVarState m a -> Maybe a+      g MVarEmpty{}    = Nothing+      g (MVarFull a _) = Just a++instance MonadLabelledMVar (IOSim s) where+  labelMVar = labelMVarDefault+ data Async s a = Async !IOSimThreadId (STM s (Either SomeException a))  instance Eq (Async s a) where@@ -665,10 +686,10 @@   getMonotonicTimeNSec = IOSim $ oneShot $ \k -> GetMonoTime (k . conv)     where       -- convert time in picoseconds to nanoseconds-      conv :: Time -> Word64-      conv (Time d) = fromIntegral (diffTimeToPicoseconds d `div` 1_000)+      conv :: SI.Time -> Word64+      conv (SI.Time d) = fromIntegral (diffTimeToPicoseconds d `div` 1_000) -instance MonadMonotonicTime (IOSim s) where+instance SI.MonadMonotonicTime (IOSim s) where   getMonotonicTime = IOSim $ oneShot $ \k -> GetMonoTime k  instance MonadTime (IOSim s) where@@ -781,14 +802,14 @@ data SimEvent     -- | Used when using `IOSim`.   = SimEvent {-      seTime        :: !Time,+      seTime        :: !SI.Time,       seThreadId    :: !IOSimThreadId,       seThreadLabel :: !(Maybe ThreadLabel),       seType        :: !SimEventType     }     -- | Only used for /IOSimPOR/   | SimPOREvent {-      seTime        :: !Time,+      seTime        :: !SI.Time,       seThreadId    :: !IOSimThreadId,       seStep        :: !Int,       seThreadLabel :: !(Maybe ThreadLabel),@@ -808,29 +829,25 @@            -> SimEvent            -> String -ppSimEvent timeWidth tidWidth tLabelWidth SimEvent {seTime = Time time, seThreadId, seThreadLabel, seType} =+ppSimEvent timeWidth tidWidth tLabelWidth SimEvent {seTime = SI.Time time, seThreadId, seThreadLabel, seType} =     printf "%-*s - %-*s %-*s - %s"            timeWidth            (show time)            tidWidth            (ppIOSimThreadId seThreadId)            tLabelWidth-           threadLabel+           (fromMaybe "" seThreadLabel)            (ppSimEventType seType)-  where-    threadLabel = fromMaybe "" seThreadLabel -ppSimEvent timeWidth tidWidth tLableWidth SimPOREvent {seTime = Time time, seThreadId, seStep, seThreadLabel, seType} =+ppSimEvent timeWidth tidWidth tLableWidth SimPOREvent {seTime = SI.Time time, seThreadId, seStep, seThreadLabel, seType} =     printf "%-*s - %-*s %-*s - %s"            timeWidth            (show time)            tidWidth            (ppStepId (seThreadId, seStep))            tLableWidth-           threadLabel+           (fromMaybe "" seThreadLabel)            (ppSimEventType seType)-  where-    threadLabel = fromMaybe "" seThreadLabel  ppSimEvent _ _ _ (SimRacesFound controls) =     "RacesFound "++show controls@@ -838,11 +855,11 @@  -- | A result type of a simulation. data SimResult a-    = MainReturn    !Time !(Labelled IOSimThreadId) a ![Labelled IOSimThreadId]+    = MainReturn    !SI.Time !(Labelled IOSimThreadId) a ![Labelled IOSimThreadId]     -- ^ Return value of the main thread.-    | MainException !Time !(Labelled IOSimThreadId) SomeException ![Labelled IOSimThreadId]+    | MainException !SI.Time !(Labelled IOSimThreadId) SomeException ![Labelled IOSimThreadId]     -- ^ Exception thrown by the main thread.-    | Deadlock      !Time ![Labelled IOSimThreadId]+    | Deadlock      !SI.Time ![Labelled IOSimThreadId]     -- ^ Deadlock discovered in the simulation.  Deadlocks are discovered if     -- simply the simulation cannot do any progress in a given time slot and     -- there's no event which would advance the time.@@ -860,7 +877,7 @@             -> SimResult a             -> String ppSimResult timeWidth tidWidth thLabelWidth r = case r of-    MainReturn (Time time) tid a tids ->+    MainReturn (SI.Time time) tid a tids ->       printf "%-*s - %-*s %-*s - %s %s"              timeWidth              (show time)@@ -870,7 +887,7 @@              (fromMaybe "" $ l_label tid)              ("MainReturn " ++ show a)              ("[" ++ intercalate "," (ppLabelled ppIOSimThreadId `map` tids) ++ "]")-    MainException (Time time) tid e tids ->+    MainException (SI.Time time) tid e tids ->       printf "%-*s - %-*s %-*s - %s %s"              timeWidth              (show time)@@ -880,7 +897,7 @@              (fromMaybe "" $ l_label tid)              ("MainException " ++ show e)              ("[" ++ intercalate "," (ppLabelled ppIOSimThreadId `map` tids) ++ "]")-    Deadlock (Time time) tids ->+    Deadlock (SI.Time time) tids ->       printf "%-*s - %-*s %-*s - %s %s"              timeWidth              (show time)@@ -917,12 +934,12 @@         bimaximum       . bimap (const (Max 0, Max 0, Max 0))               (\a -> case a of-                SimEvent {seTime = Time time, seThreadId, seThreadLabel} ->+                SimEvent {seTime = SI.Time time, seThreadId, seThreadLabel} ->                   ( Max (length (show time))                   , Max (length (show (seThreadId)))                   , Max (length seThreadLabel)                   )-                SimPOREvent {seTime = Time time, seThreadId, seThreadLabel} ->+                SimPOREvent {seTime = SI.Time time, seThreadId, seThreadLabel} ->                   ( Max (length (show time))                   , Max (length (show (seThreadId)))                   , Max (length seThreadLabel)@@ -935,7 +952,7 @@  -- | Like 'ppTrace' but does not show the result value. ---ppTrace_ :: SimTrace a -> String+ppTrace_ :: Trace.Trace a SimEvent -> String ppTrace_ tr = Trace.ppTrace                 (const "")                 (ppSimEvent timeWidth tidWidth labelWidth)@@ -971,13 +988,13 @@         . Trace.toList  -pattern SimTrace :: Time -> IOSimThreadId -> Maybe ThreadLabel -> SimEventType -> SimTrace a+pattern SimTrace :: SI.Time -> IOSimThreadId -> Maybe ThreadLabel -> SimEventType -> SimTrace a                  -> SimTrace a pattern SimTrace time threadId threadLabel traceEvent trace =     Trace.Cons (SimEvent time threadId threadLabel traceEvent)                trace -pattern SimPORTrace :: Time -> IOSimThreadId -> Int -> Maybe ThreadLabel -> SimEventType -> SimTrace a+pattern SimPORTrace :: SI.Time -> IOSimThreadId -> Int -> Maybe ThreadLabel -> SimEventType -> SimTrace a                     -> SimTrace a pattern SimPORTrace time threadId step threadLabel traceEvent trace =     Trace.Cons (SimPOREvent time threadId step threadLabel traceEvent)@@ -989,15 +1006,15 @@     Trace.Cons (SimRacesFound controls)                trace -pattern TraceMainReturn :: Time -> Labelled IOSimThreadId -> a -> [Labelled IOSimThreadId]+pattern TraceMainReturn :: SI.Time -> Labelled IOSimThreadId -> a -> [Labelled IOSimThreadId]                         -> SimTrace a pattern TraceMainReturn time tid a threads = Trace.Nil (MainReturn time tid a threads) -pattern TraceMainException :: Time -> Labelled IOSimThreadId -> SomeException -> [Labelled IOSimThreadId]+pattern TraceMainException :: SI.Time -> Labelled IOSimThreadId -> SomeException -> [Labelled IOSimThreadId]                            -> SimTrace a pattern TraceMainException time tid err threads = Trace.Nil (MainException time tid err threads) -pattern TraceDeadlock :: Time -> [Labelled IOSimThreadId]+pattern TraceDeadlock :: SI.Time -> [Labelled IOSimThreadId]                       -> SimTrace a pattern TraceDeadlock time threads = Trace.Nil (Deadlock time threads) @@ -1063,22 +1080,22 @@   -- Timeouts, Timers & Delays   -- -  | EventThreadDelay        TimeoutId Time+  | EventThreadDelay        TimeoutId SI.Time   -- ^ thread delayed   | EventThreadDelayFired   TimeoutId   -- ^ thread woken up after a delay -  | EventTimeoutCreated        TimeoutId IOSimThreadId Time+  | EventTimeoutCreated        TimeoutId IOSimThreadId SI.Time   -- ^ new timeout created (via `timeout`)   | EventTimeoutFired          TimeoutId   -- ^ timeout fired -  | EventRegisterDelayCreated TimeoutId TVarId Time+  | EventRegisterDelayCreated TimeoutId TVarId SI.Time   -- ^ registered delay (via `registerDelay`)   | EventRegisterDelayFired TimeoutId   -- ^ registered delay fired -  | EventTimerCreated         TimeoutId TVarId Time+  | EventTimerCreated         TimeoutId TVarId SI.Time   -- ^ a new 'Timeout' created (via `newTimeout`)   | EventTimerCancelled       TimeoutId   -- ^ a 'Timeout' was cancelled (via `cancelTimeout`)@@ -1209,22 +1226,7 @@              ppEffect eff ]   EventRaces a -> show a --- | A labelled value. ----- For example 'labelThread' or `labelTVar' will insert a label to `IOSimThreadId`--- (or `TVarId`).-data Labelled a = Labelled {-    l_labelled :: !a,-    l_label    :: !(Maybe String)-  }-  deriving (Eq, Ord, Generic)-  deriving Show via Quiet (Labelled a)--ppLabelled :: (a -> String) -> Labelled a -> String-ppLabelled pp Labelled { l_labelled = a, l_label = Nothing  } = pp a-ppLabelled pp Labelled { l_labelled = a, l_label = Just lbl } = concat ["Labelled ", pp a, " ", lbl]---- -- Executing STM Transactions -- @@ -1248,7 +1250,7 @@                         ![SomeTVar s] -- ^ created tvars                         ![Dynamic]                         ![String]-                        !TVarId -- updated TVarId name supply+                        !VarId -- updated TVarId name supply         -- | A blocked transaction reports the vars that were read so that the        -- scheduler can block the thread on those vars.
src/Control/Monad/IOSimPOR/Internal.hs view
@@ -1,21 +1,13 @@-{-# LANGUAGE BangPatterns              #-}-{-# LANGUAGE CPP                       #-}-{-# LANGUAGE DerivingVia               #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleContexts          #-}-{-# LANGUAGE FlexibleInstances         #-}-{-# LANGUAGE GADTSyntax                #-}-{-# LANGUAGE MultiParamTypeClasses     #-}-{-# LANGUAGE NamedFieldPuns            #-}-{-# LANGUAGE RankNTypes                #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE DerivingVia     #-} -- only used to construct records if its clear to do so-{-# LANGUAGE RecordWildCards           #-}-{-# LANGUAGE ScopedTypeVariables       #-}-{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies    #-}  -- incomplete uni patterns in 'schedule' (when interpreting 'StmTxCommitted') -- and 'reschedule'. {-# OPTIONS_GHC -Wno-incomplete-uni-patterns -Wno-unused-matches #-}+{-# OPTIONS_GHC -Wno-orphans #-} #if __GLASGOW_HASKELL__ >= 908 -- We use partial functions from `Data.List`. {-# OPTIONS_GHC -Wno-x-partial #-}@@ -53,14 +45,16 @@  import Data.Dynamic import Data.Foldable (foldlM, traverse_)+import Data.HashPSQ (HashPSQ)+import Data.HashPSQ qualified as PSQ+import Data.IntPSQ (IntPSQ)+import Data.IntPSQ qualified as IPSQ import Data.List qualified as List import Data.List.Trace qualified as Trace import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Maybe (mapMaybe) import Data.Ord-import Data.OrdPSQ (OrdPSQ)-import Data.OrdPSQ qualified as PSQ import Data.Set (Set) import Data.Set qualified as Set import Data.Time (UTCTime (..), fromGregorian)@@ -77,14 +71,19 @@ import Control.Monad.Class.MonadSTM hiding (STM) import Control.Monad.Class.MonadSTM.Internal (TMVarDefault (TMVar)) import Control.Monad.Class.MonadThrow as MonadThrow-import Control.Monad.Class.MonadTime+import Control.Monad.Class.MonadTime (NominalDiffTime)+import Control.Monad.Class.MonadTime qualified as Time+import Control.Monad.Class.MonadTime.SI qualified as SI import Control.Monad.Class.MonadTimer.SI (TimeoutState (..))  import Control.Monad.IOSim.InternalTypes-import Control.Monad.IOSim.Types hiding (SimEvent (SimEvent), Trace (SimTrace))+import Control.Monad.IOSim.Types hiding (SimEvent (SimEvent), Time (..),+           Trace (SimTrace)) import Control.Monad.IOSim.Types (SimEvent) import Control.Monad.IOSimPOR.Timeout (unsafeTimeout) import Control.Monad.IOSimPOR.Types+import Data.Coerce (Coercible, coerce)+import Data.Hashable  -- -- Simulation interpreter@@ -178,9 +177,11 @@      -- ^ `timeout` timer run by `IOSimThreadId` which was assigned the given      -- `TimeoutId` (only used to report in a trace). -type RunQueue   = OrdPSQ (Down IOSimThreadId) (Down IOSimThreadId) ()-type Timeouts s = OrdPSQ TimeoutId Time (TimerCompletionInfo s)+instance Hashable a => Hashable (Down a) +type RunQueue   = HashPSQ (Down IOSimThreadId) (Down IOSimThreadId) ()+type Timeouts s = IntPSQ SI.Time (TimerCompletionInfo s)+ -- | Internal state. -- data SimState s a = SimState {@@ -189,13 +190,13 @@        -- and blocked threads.        threads          :: !(Map IOSimThreadId (Thread s a)),        -- | current time-       curTime          :: !Time,+       curTime          :: !SI.Time,        -- | ordered list of timers and timeouts        timers           :: !(Timeouts s),        -- | timeout locks in order to synchronize the timeout handler and the        -- main thread        clocks           :: !(Map ClockId UTCTime),-       nextVid          :: !TVarId,     -- ^ next unused 'TVarId'+       nextVid          :: !VarId,     -- ^ next unused 'TVarId'        nextTmid         :: !TimeoutId,  -- ^ next unused 'TimeoutId'        -- | previous steps (which we may race with).        -- Note this is *lazy*, so that we don't compute races we will not reverse.@@ -214,10 +215,10 @@     SimState {       runqueue = PSQ.empty,       threads  = Map.empty,-      curTime  = Time 0,-      timers   = PSQ.empty,+      curTime  = SI.Time 0,+      timers   = IPSQ.empty,       clocks   = Map.singleton (ClockId []) epoch1970,-      nextVid  = TVarId 0,+      nextVid  = 0,       nextTmid = TimeoutId 0,       races    = noRaces,       control  = ControlDefault,@@ -240,16 +241,13 @@     assert (PSQ.fold' (\(Down tid) _ _ a -> tid `Map.member` threads && a) True runqueue)   . assert (and [ (isThreadBlocked t || isThreadDone t) == not (Down (threadId t) `PSQ.member` runqueue)                 | t <- Map.elems threads ])-  . assert (and (zipWith (\(Down tid, _, _) (Down tid', _, _) -> tid > tid')-                         (PSQ.toList runqueue)-                         (drop 1 (PSQ.toList runqueue))))   . assert (and [ threadClockId t `Map.member` clocks                 | t <- Map.elems threads ])  -- | Interpret the simulation monotonic time as a 'NominalDiffTime' since -- the start.-timeSinceEpoch :: Time -> NominalDiffTime-timeSinceEpoch (Time t) = fromRational (toRational t)+timeSinceEpoch :: SI.Time -> NominalDiffTime+timeSinceEpoch (SI.Time t) = fromRational (toRational t)   -- | Insert thread into `runqueue`.@@ -372,7 +370,7 @@        DelayFrame tmid k ctl' -> do         let thread' = thread { threadControl = ThreadControl k ctl' }-            timers' = PSQ.delete tmid timers+            timers' = IPSQ.delete (coerce tmid) timers         schedule thread' simstate { timers = timers' }      Throw e -> case unwindControlStack e thread timers of@@ -453,15 +451,15 @@     GetWallTime k -> do       let clockid  = threadClockId thread           clockoff = clocks Map.! clockid-          walltime = timeSinceEpoch time `addUTCTime` clockoff+          walltime = timeSinceEpoch time `Time.addUTCTime` clockoff           thread'  = thread { threadControl = ThreadControl (k walltime) ctl }       schedule thread' simstate      SetWallTime walltime' k -> do       let clockid   = threadClockId thread           clockoff  = clocks Map.! clockid-          walltime  = timeSinceEpoch time `addUTCTime` clockoff-          clockoff' = addUTCTime (diffUTCTime walltime' walltime) clockoff+          walltime  = timeSinceEpoch time `Time.addUTCTime` clockoff+          clockoff' = (walltime' `Time.diffUTCTime` walltime) `Time.addUTCTime` clockoff           thread'   = thread { threadControl = ThreadControl k ctl }           simstate' = simstate { clocks = Map.insert clockid clockoff' clocks }       schedule thread' simstate'@@ -480,9 +478,9 @@       error "schedule: StartTimeout: Impossible happened"      StartTimeout d action' k -> do-      lock <- TMVar <$> execNewTVar nextVid (Just $! "lock-" ++ show nextTmid) Nothing+      lock <- TMVar <$> execNewTVar (TMVarId nextVid) (Just $! "lock-" ++ show nextTmid) Nothing       let expiry    = d `addTime` time-          timers'   = PSQ.insert nextTmid expiry (TimerTimeout tid nextTmid lock) timers+          timers'   = IPSQ.insert (coerce nextTmid) expiry (TimerTimeout tid nextTmid lock) timers           thread'   = thread { threadControl =                                  ThreadControl action'                                                (TimeoutFrame nextTmid lock k ctl)@@ -493,33 +491,33 @@      UnregisterTimeout tmid k -> do       let thread' = thread { threadControl = ThreadControl k ctl }-      schedule thread' simstate { timers = PSQ.delete tmid timers }+      schedule thread' simstate { timers = IPSQ.delete (coerce tmid) timers }      RegisterDelay d k | d < 0 -> do-      tvar <- execNewTVar nextVid+      tvar <- execNewTVar (TVarId nextVid)                           (Just $! "<<timeout " ++ show (unTimeoutId nextTmid) ++ ">>")                           True       modifySTRef (tvarVClock tvar) (leastUpperBoundVClock vClock)       let !expiry  = d `addTime` time           !thread' = thread { threadControl = ThreadControl (k tvar) ctl }       trace <- schedule thread' simstate { nextVid = succ nextVid }-      return (SimPORTrace time tid tstep tlbl (EventRegisterDelayCreated nextTmid nextVid expiry) $+      return (SimPORTrace time tid tstep tlbl (EventRegisterDelayCreated nextTmid (TVarId nextVid) expiry) $               SimPORTrace time tid tstep tlbl (EventRegisterDelayFired nextTmid) $               trace)      RegisterDelay d k -> do-      tvar <- execNewTVar nextVid+      tvar <- execNewTVar (TVarId nextVid)                           (Just $! "<<timeout " ++ show (unTimeoutId nextTmid) ++ ">>")                           False       modifySTRef (tvarVClock tvar) (leastUpperBoundVClock vClock)       let !expiry  = d `addTime` time-          !timers' = PSQ.insert nextTmid expiry (TimerRegisterDelay tvar) timers+          !timers' = IPSQ.insert (coerce nextTmid) expiry (TimerRegisterDelay tvar) timers           !thread' = thread { threadControl = ThreadControl (k tvar) ctl }       trace <- schedule thread' simstate { timers   = timers'                                          , nextVid  = succ nextVid                                          , nextTmid = succ nextTmid }       return (SimPORTrace time tid tstep tlbl-                (EventRegisterDelayCreated nextTmid nextVid expiry) trace)+                (EventRegisterDelayCreated nextTmid (TVarId nextVid) expiry) trace)      ThreadDelay d k | d < 0 -> do       let expiry    = d `addTime` time@@ -532,7 +530,7 @@      ThreadDelay d k -> do       let expiry  = d `addTime` time-          timers' = PSQ.insert nextTmid expiry (TimerThreadDelay tid nextTmid) timers+          timers' = IPSQ.insert (coerce nextTmid) expiry (TimerThreadDelay tid nextTmid) timers           thread' = thread { threadControl = ThreadControl (Return ()) (DelayFrame nextTmid k ctl) }       trace <- deschedule (Blocked BlockedOnDelay) thread'                           simstate { timers   = timers',@@ -547,31 +545,32 @@           expiry  = d `addTime` time           thread' = thread { threadControl = ThreadControl (k t) ctl }       trace <- schedule thread' simstate { nextTmid = succ nextTmid }-      return (SimPORTrace time tid tstep tlbl (EventTimerCreated nextTmid nextVid expiry) $+      return (SimPORTrace time tid tstep tlbl (EventTimerCreated nextTmid (TVarId nextVid) expiry) $               SimPORTrace time tid tstep tlbl (EventTimerCancelled nextTmid) $               trace)      NewTimeout d k -> do-      tvar  <- execNewTVar nextVid+      tvar  <- execNewTVar (TVarId nextVid)                            (Just $! "<<timeout-state " ++ show (unTimeoutId nextTmid) ++ ">>")                            TimeoutPending       modifySTRef (tvarVClock tvar) (leastUpperBoundVClock vClock)       let expiry  = d `addTime` time           t       = Timeout tvar nextTmid-          timers' = PSQ.insert nextTmid expiry (Timer tvar) timers+          timers' = IPSQ.insert (coerce nextTmid) expiry (Timer tvar) timers           thread' = thread { threadControl = ThreadControl (k t) ctl }       trace <- schedule thread' simstate { timers   = timers'-                                          , nextVid  = succ (succ nextVid)-                                          , nextTmid = succ nextTmid }-      return (SimPORTrace time tid tstep tlbl (EventTimerCreated nextTmid nextVid expiry) trace)+                                         , nextVid  = succ (succ nextVid)+                                         , nextTmid = succ nextTmid }+      return (SimPORTrace time tid tstep tlbl (EventTimerCreated nextTmid (TVarId nextVid) expiry) trace)      CancelTimeout (Timeout tvar tmid) k -> do-      let timers' = PSQ.delete tmid timers+      let timers' = IPSQ.delete (coerce tmid) timers       written <- execAtomically' (runSTM $ writeTVar tvar TimeoutCancelled)+      written' <- mapM someTVarToLabelled written       (wakeup, wokeby) <- threadsUnblockedByWrites written       mapM_ (\(SomeTVar var) -> unblockAllThreadsFromTVar var) written       let effect' = effect-                 <> writeEffects written+                 <> writeEffects written'                  <> wakeupEffects wakeup           thread' = thread { threadControl = ThreadControl k ctl                            , threadEffect  = effect'@@ -636,10 +635,12 @@           (wakeup, wokeby) <- threadsUnblockedByWrites written           mapM_ (\(SomeTVar tvar) -> unblockAllThreadsFromTVar tvar) written           vClockRead <- leastUpperBoundTVarVClocks read+          read' <- mapM someTVarToLabelled read+          written' <- mapM someTVarToLabelled written           let vClock'     = vClock `leastUpperBoundVClock` vClockRead               effect'     = effect-                         <> readEffects read-                         <> writeEffects written+                         <> readEffects read'+                         <> writeEffects written'                          <> wakeupEffects unblocked               thread'     = thread { threadControl = ThreadControl (k x) ctl,                                      threadVClock  = vClock',@@ -648,12 +649,12 @@                simstate') = unblockThreads True vClock' wakeup simstate           sequence_ [ modifySTRef (tvarVClock r) (leastUpperBoundVClock vClock')                     | SomeTVar r <- created ++ written ]-          written' <- traverse (\(SomeTVar tvar) -> labelledTVarId tvar) written+          written'' <- traverse (\(SomeTVar tvar) -> labelledTVarId tvar) written           created' <- traverse (\(SomeTVar tvar) -> labelledTVarId tvar) created           -- We deschedule a thread after a transaction... another may have woken up.           !trace <- deschedule Yield thread' simstate' { nextVid  = nextVid' }           return $-            SimPORTrace time tid tstep tlbl (EventTxCommitted written' created' (Just effect')) $+            SimPORTrace time tid tstep tlbl (EventTxCommitted written'' created' (Just effect')) $             traceMany               [ (time, tid', (-1), tlbl', EventTxWakeup vids')               | tid' <- unblocked@@ -674,7 +675,8 @@         StmTxAborted read e -> do           -- schedule this thread to immediately raise the exception           vClockRead <- leastUpperBoundTVarVClocks read-          let effect' = effect <> readEffects read+          read' <- mapM someTVarToLabelled read+          let effect' = effect <> readEffects read'               thread' = thread { threadControl = ThreadControl (Throw e) ctl,                                  threadVClock  = vClock `leastUpperBoundVClock` vClockRead,                                  threadEffect  = effect' }@@ -686,7 +688,8 @@           mapM_ (\(SomeTVar tvar) -> blockThreadOnTVar tid tvar) read           vids <- traverse (\(SomeTVar tvar) -> labelledTVarId tvar) read           vClockRead <- leastUpperBoundTVarVClocks read-          let effect' = effect <> readEffects read+          read' <- mapM someTVarToLabelled read+          let effect' = effect <> readEffects read'               thread' = thread { threadVClock  = vClock `leastUpperBoundVClock` vClockRead,                                  threadEffect  = effect' }           !trace <- deschedule (Blocked BlockedOnSTM) thread' simstate@@ -703,6 +706,13 @@                            , threadLabel   = Just l }       schedule thread' simstate +    GetThreadLabel tid' k -> do+      let tlbl' | tid' == tid = tlbl+                | otherwise   = tid' `Map.lookup` threads+                            >>= threadLabel+          thread' = thread { threadControl = ThreadControl (k tlbl') ctl }+      schedule thread' simstate+     LabelThread tid' l k -> do       let thread'  = thread { threadControl = ThreadControl k ctl }           threads' = Map.adjust (\t -> t { threadLabel = Just l }) tid' threads@@ -814,6 +824,21 @@         | otherwise              -> False       MaskedUninterruptible      -> False ++-- | Deschedule a thread.+--+-- A thread is descheduled, which marks a boundary of a `Step` when:+--+-- * forking a new thread+-- * thread termination+-- * setting the masking state to interruptible+-- * popping masking frame (which resets masking state)+-- * starting or cancelling a timeout+-- * thread delays+-- * on committed or blocked, but not aborted STM transactions+-- * on blocking or non-blocking `throwTo`+-- * unhandled exception in a (non-main) thread+-- deschedule :: Deschedule -> Thread s a -> SimState s a -> ST s (SimTrace a)  deschedule Yield thread@Thread { threadId     = tid,@@ -1264,34 +1289,34 @@           _ -> unwind maskst ctl timers'       where         -- Remove the timeout associated with the 'TimeoutFrame'.-        timers' = PSQ.delete tmid timers+        timers' = IPSQ.delete (coerce tmid) timers      unwind maskst (DelayFrame tmid _k ctl) timers =         unwind maskst ctl timers'       where         -- Remove the timeout associated with the 'DelayFrame'.-        timers' = PSQ.delete tmid timers+        timers' = IPSQ.delete (coerce tmid) timers      atLeastInterruptibleMask :: MaskingState -> MaskingState     atLeastInterruptibleMask Unmasked = MaskedInterruptible     atLeastInterruptibleMask ms       = ms  -removeMinimums :: (Ord k, Ord p)-               => OrdPSQ k p a-               -> Maybe ([k], p, [a], OrdPSQ k p a)-removeMinimums = \psq ->-    case PSQ.minView psq of+removeMinimums :: (Coercible Int k, Ord p)+               => IntPSQ p a+               -> Maybe ([k], p, [a], IntPSQ p a)+removeMinimums = \psq -> coerce $+    case IPSQ.minView psq of       Nothing              -> Nothing       Just (k, p, x, psq') -> Just (collectAll [k] p [x] psq')   where     collectAll ks p xs psq =-      case PSQ.minView psq of+      case IPSQ.minView psq of         Just (k, p', x, psq')           | p == p' -> collectAll (k:ks) p (x:xs) psq'         _           -> (reverse ks, p, reverse xs, psq) -traceMany :: [(Time, IOSimThreadId, Int, Maybe ThreadLabel, SimEventType)]+traceMany :: [(SI.Time, IOSimThreadId, Int, Maybe ThreadLabel, SimEventType)]           -> SimTrace a -> SimTrace a traceMany []                                   trace = trace traceMany ((time, tid, tstep, tlbl, event):ts) trace =@@ -1343,10 +1368,10 @@ --  execAtomically :: forall s a c.-                  Time+                  SI.Time                -> IOSimThreadId                -> Maybe ThreadLabel-               -> TVarId+               -> VarId                -> StmA s a                -> (StmTxResult s a -> ST s (SimTrace c))                -> ST s (SimTrace c)@@ -1359,7 +1384,7 @@        -> Map TVarId (SomeTVar s)  -- set of vars written        -> [SomeTVar s]             -- vars written in order (no dups)        -> [SomeTVar s]             -- vars created in order-       -> TVarId                   -- var fresh name supply+       -> VarId                   -- var fresh name supply        -> StmA s b        -> ST s (SimTrace c)     go !ctl !read !written !writtenSeq !createdSeq !nextVid !action =@@ -1458,8 +1483,8 @@         let ctl' = BranchFrame (OrElseStmA b) k written writtenSeq createdSeq ctl         go ctl' read Map.empty [] [] nextVid a -      NewTVar !mbLabel x k -> do-        !v <- execNewTVar nextVid mbLabel x+      NewTVar mkId !mbLabel x k -> do+        !v <- execNewTVar (mkId nextVid) mbLabel x         -- record a write to the TVar so we know to update its VClock         let written' = Map.insert (tvarId v) (SomeTVar v) written         -- save the value: it will be committed or reverted@@ -1548,14 +1573,14 @@   execNewTVar :: TVarId -> Maybe String -> a -> ST s (TVar s a)-execNewTVar nextVid !mbLabel x = do+execNewTVar !tvarId !mbLabel x = do     tvarLabel   <- newSTRef mbLabel     tvarCurrent <- newSTRef x     tvarUndo    <- newSTRef []     tvarBlocked <- newSTRef ([], Set.empty)     tvarVClock  <- newSTRef bottomVClock     tvarTrace   <- newSTRef Nothing-    return TVar {tvarId = nextVid, tvarLabel,+    return TVar {tvarId, tvarLabel,                  tvarCurrent, tvarUndo, tvarBlocked, tvarVClock,                  tvarTrace} @@ -1837,7 +1862,8 @@                         $ activeRaces                         )                      ++ completeRaces-  in Races{ activeRaces = activeRaces', completeRaces = completeRaces' }+  in Races{ activeRaces   = activeRaces',+            completeRaces = completeRaces' }   -- When a thread terminates, we remove it from the concurrent thread
src/Control/Monad/IOSimPOR/QuickCheckUtils.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE BangPatterns #-}- {-# OPTIONS_GHC -Wno-name-shadowing #-}  module Control.Monad.IOSimPOR.QuickCheckUtils where
src/Control/Monad/IOSimPOR/Types.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE NamedFieldPuns #-} module Control.Monad.IOSimPOR.Types   ( -- * Effects     Effect (..)@@ -38,8 +37,8 @@ -- execution step.  Only used by *IOSimPOR*. -- data Effect = Effect {-    effectReads  :: !(Set TVarId),-    effectWrites :: !(Set TVarId),+    effectReads  :: !(Set (Labelled TVarId)),+    effectWrites :: !(Set (Labelled TVarId)),     effectForks  :: !(Set IOSimThreadId),     effectThrows :: ![IOSimThreadId],     effectWakeup :: !(Set IOSimThreadId)@@ -50,11 +49,11 @@ ppEffect Effect { effectReads, effectWrites, effectForks, effectThrows, effectWakeup } =   "Effect { " ++     concat (List.intersperse ", " $-           [ "reads = "  ++ show effectReads  | not (null effectReads)  ]-        ++ [ "writes = " ++ show effectWrites | not (null effectWrites) ]-        ++ [ "forks = "  ++ ppList ppIOSimThreadId (Set.toList effectForks)  | not (null effectForks)  ]-        ++ [ "throws = " ++ ppList ppIOSimThreadId             effectThrows  | not (null effectThrows) ]-        ++ [ "wakeup = " ++ ppList ppIOSimThreadId (Set.toList effectWakeup) | not (null effectWakeup) ])+           [ "reads = "  ++ ppList (ppLabelled show) (Set.toList effectReads)  | not (null effectReads)  ]+        ++ [ "writes = " ++ ppList (ppLabelled show) (Set.toList effectWrites) | not (null effectWrites) ]+        ++ [ "forks = "  ++ ppList ppIOSimThreadId (Set.toList effectForks)    | not (null effectForks)  ]+        ++ [ "throws = " ++ ppList ppIOSimThreadId             effectThrows    | not (null effectThrows) ]+        ++ [ "wakeup = " ++ ppList ppIOSimThreadId (Set.toList effectWakeup)   | not (null effectWakeup) ])         ++ " }"  @@ -69,17 +68,11 @@ -- Effect smart constructors -- --- readEffect :: SomeTVar s -> Effect--- readEffect r = mempty{effectReads = Set.singleton $ someTvarId r }--readEffects :: [SomeTVar s] -> Effect-readEffects rs = mempty{effectReads = Set.fromList (map someTvarId rs)}---- writeEffect :: SomeTVar s -> Effect--- writeEffect r = mempty{effectWrites = Set.singleton $ someTvarId r }+readEffects :: [Labelled (SomeTVar s)] -> Effect+readEffects rs = mempty{effectReads = Set.fromList (map (someTvarId <$>) rs)} -writeEffects :: [SomeTVar s] -> Effect-writeEffects rs = mempty{effectWrites = Set.fromList (map someTvarId rs)}+writeEffects :: [Labelled (SomeTVar s)] -> Effect+writeEffects rs = mempty{effectWrites = Set.fromList (map (someTvarId <$>) rs)}  forkEffect :: IOSimThreadId -> Effect forkEffect tid = mempty{effectForks = Set.singleton tid}@@ -171,16 +164,20 @@   showsPrec d (ScheduleMod tgt ctrl insertion) =     showParen (d>10) $       showString "ScheduleMod " .-      showsPrec 11 tgt .+      showParen True (showString (ppStepId tgt)) .       showString " " .       showsPrec 11 ctrl .-      showString " " .-      showsPrec 11 insertion+      showString " [" .+      showString (List.intercalate "," (map ppStepId insertion)) .+      showString "]"  -- -- Steps -- +-- | A unit of execution.  `deschedule` marks a boundary of a `Step`, see it's+-- haddocks.+-- data Step = Step {     stepThreadId :: !IOSimThreadId,     stepStep     :: !Int,@@ -223,11 +220,18 @@ -- Races -- -data Races = Races { -- These steps may still race with future steps-                     activeRaces   :: ![StepInfo],-                     -- These steps cannot be concurrent with future steps-                     completeRaces :: ![StepInfo]-                   }+-- | Information about all discovered races in a simulation categorised as+-- active and complete races.+--+-- See 'normalizeRaces' how we split `StepInfo` into the two categories.+--+data Races = Races {+    -- | These steps may still race with future steps.+    activeRaces   :: ![StepInfo],++    -- | These steps cannot be concurrent with future steps.+    completeRaces :: ![StepInfo]+  }   deriving Show  noRaces :: Races
src/Data/Deque/Strict.hs view
@@ -1,6 +1,4 @@-{-# LANGUAGE CPP        #-}-{-# LANGUAGE LambdaCase #-}-+{-# LANGUAGE CPP #-} -- | A minimal implementation of a strict deque. -- module Data.Deque.Strict where
src/Data/List/Trace.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE CPP           #-}-{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE CPP #-}  module Data.List.Trace   ( Trace (..)
test/Test/Control/Concurrent/Class/MonadMVar.hs view
@@ -1,13 +1,11 @@-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections       #-}+{-# LANGUAGE GADTs #-}  module Test.Control.Concurrent.Class.MonadMVar where  import Control.Concurrent.Class.MonadMVar import Control.Monad.Class.MonadAsync import Control.Monad.Class.MonadFork+import Control.Monad.Class.MonadTest import Control.Monad.Class.MonadTime.SI import Control.Monad.Class.MonadTimer.SI import Data.Bifoldable (bifoldMap)@@ -64,6 +62,7 @@       [ testCase "empty MVar is empty"    unit_isEmptyMVar_empty_sim       , testCase "full MVar is not empty" unit_isEmptyMVar_full_sim       ]+    , testProperty "takeMVar is exception safe" prop_takeMVar_exception_safe     ]  @@ -309,6 +308,29 @@ unit_isEmptyMVar_full_sim =     assertBool "full mvar must not be empty" $     runSimOrThrow (prop_isEmptyMVar False)++--+-- takeMVar is exception safe+--+prop_takeMVar_exception_safe :: Property+prop_takeMVar_exception_safe =+  exploreSimTrace id (do+      exploreRaces+      mv <- newMVar (0 :: Int)+      t1 <- async $ void $ withMVar mv (\v -> pure (v + 1, ()))+      t2 <- async $ void $ do+        _ <- withMVar mv (\v -> pure (v + 1, ()))+        withMVar mv (\v -> pure (v + 1, ()))+      t3 <- async $ cancel t1+      wait t3+      wait t2+      wait t1+  ) (\_ trace ->+       case traceResult False trace of+         Left FailureDeadlock{} ->+           counterexample (ppTrace trace) $ property False+         _ -> property True+    )  -- -- Utils
test/Test/Control/Monad/IOSim.hs view
@@ -1,10 +1,4 @@-{-# LANGUAGE CPP                 #-}-{-# LANGUAGE ConstraintKinds     #-}-{-# LANGUAGE DeriveFunctor       #-}-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE NumericUnderscores  #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE CPP #-}  {-# OPTIONS_GHC -Wno-orphans #-} @@ -192,6 +186,10 @@       , testProperty "maintains FIFO order IO" prop_flushTBQueueOrder_IO       , testProperty "maintains FIFO order IOSim" prop_flushTBQueueOrder_IOSim       ]+    , testGroup "tryReadTBQueue"+      [ testProperty "works correctly when the queue is empty IO" prop_tryReadEmptyTBQueue_IO+      , testProperty "works correctly when the queue is empty IOSim" prop_tryReadEmptyTBQueue_IOSim+      ]     ]   ] @@ -889,7 +887,6 @@       , MonadMask  m       , MonadThrow (STM m)       , MonadSay   m-      , MonadMaskingState m       )  instance Arbitrary DiffTime where@@ -1463,6 +1460,24 @@     q <- newTBQueue (1 + fromIntegral (length entries))     forM_ entries $ writeTBQueue q     flushTBQueue q++prop_tryReadEmptyTBQueue_IO :: Bool -> Property+prop_tryReadEmptyTBQueue_IO sndRead =+  ioProperty $ tryReadEmptyTBQueue sndRead++prop_tryReadEmptyTBQueue_IOSim :: Bool -> Property+prop_tryReadEmptyTBQueue_IOSim sndRead =+  runSimOrThrow $ tryReadEmptyTBQueue sndRead++tryReadEmptyTBQueue :: MonadSTM m => Bool -> m Property+tryReadEmptyTBQueue sndRead = atomically $ do+  q <- newTBQueue 10+  _ <- tryReadTBQueue q+  writeTBQueue q ()+  when sndRead $ void $ tryReadTBQueue q+  l <- lengthTBQueue q++  pure $ l === if sndRead then 0 else 1  -- -- Utils
test/Test/Control/Monad/IOSimPOR.hs view
@@ -1,10 +1,5 @@-{-# LANGUAGE CPP                   #-}-{-# LANGUAGE DeriveGeneric         #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE LambdaCase            #-}-{-# LANGUAGE PartialTypeSignatures #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE CPP           #-}+{-# LANGUAGE DeriveGeneric #-}  {-# OPTIONS_GHC -Wno-unused-top-binds #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}@@ -434,15 +429,31 @@   threadDelay 1   readTVarIO r --traceNoDuplicates :: (Testable prop1, Show a1) => ((a1 -> a2 -> a2) -> prop1) -> Property-traceNoDuplicates k = r `pseq` (k addTrace .&&. maximum (traceCounts ()) == 1)+traceNoDuplicates :: forall a b.+                     (Show a)+                  => ((a -> b -> b) -> Property)+                  -> Property+-- this NOINLINE pragma is useful for debugging if `r` didn't flow outside of+-- `traceNoDuplicate`.+{-# NOINLINE traceNoDuplicates #-}+traceNoDuplicates k = unsafePerformIO $ do+    r <- newIORef (Map.empty :: Map String Int)+    return $ r `pseq`+             (k (addTrace r) .&&. counterexample "trace counts" (maximum (Map.elems (traceCounts r)) === 1))   where-    r = unsafePerformIO $ newIORef (Map.empty :: Map String Int)-    addTrace t x = unsafePerformIO $ do-      atomicModifyIORef r (\m->(Map.insertWith (+) (show t) 1 m,()))+    addTrace :: IORef (Map String Int) -> a -> b -> b+    addTrace r t x = unsafePerformIO $ do+      let s = show t+      atomicModifyIORef r+        (\m->+          let m' = Map.insertWith (+) s 1 m+          in (m', ())+        )       return x-    traceCounts () = unsafePerformIO $ Map.elems <$> readIORef r++    traceCounts :: IORef (Map String Int) -> Map String Int+    traceCounts r = unsafePerformIO $ readIORef r+  -- | Checks that IOSimPOR is capable of analysing an infinite simulation -- lazily.
test/Test/Control/Monad/STM.hs view
@@ -1,14 +1,7 @@-{-# LANGUAGE BangPatterns               #-}-{-# LANGUAGE DataKinds                  #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE GADTs                      #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns             #-}-{-# LANGUAGE RankNTypes                 #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE StandaloneDeriving         #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE DataKinds          #-}+{-# LANGUAGE GADTs              #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies       #-}  {-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-} 
test/Test/Control/Monad/Utils.hs view
@@ -1,7 +1,3 @@-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE ScopedTypeVariables #-}- module Test.Control.Monad.Utils where  import Data.Array@@ -340,7 +336,7 @@  -- | Check that setting masking state is effective. ---prop_set_masking_state :: MonadMaskingState m+prop_set_masking_state :: MonadMask m                        => MaskingState                        -> m Property prop_set_masking_state ms =@@ -350,7 +346,7 @@  -- | Check that 'unmask' restores the masking state. ---prop_unmask :: MonadMaskingState m+prop_unmask :: MonadMask m             => MaskingState             -> MaskingState             -> m Property@@ -362,7 +358,7 @@  -- | Check that masking state is inherited by a forked thread. ---prop_fork_masking_state :: ( MonadMaskingState m+prop_fork_masking_state :: ( MonadMask m                            , MonadFork m                            , MonadSTM m                            )@@ -378,7 +374,7 @@ -- Note: unlike 'prop_unmask', 'forkIOWithUnmask's 'unmask' function will -- restore 'Unmasked' state, not the encosing masking state. ---prop_fork_unmask :: ( MonadMaskingState m+prop_fork_unmask :: ( MonadMask m                     , MonadFork m                     , MonadSTM m                     )@@ -397,8 +393,9 @@ -- | A unit test which checks the masking state in the context of a catch -- handler. ---prop_catch_throwIO_masking_state :: forall m. MonadMaskingState m-                                 => MaskingState -> m Property+prop_catch_throwIO_masking_state :: forall m. MonadMask m+                                 => MaskingState+                                 -> m Property prop_catch_throwIO_masking_state ms =     setMaskingState_ ms $ do       throwIO (userError "error")@@ -409,7 +406,7 @@ -- | Like 'prop_catch_masking_state' but using 'throwTo'. -- prop_catch_throwTo_masking_state :: forall m.-                                    ( MonadMaskingState m+                                    ( MonadMask m                                     , MonadFork m                                     )                                  => MaskingState -> m Property@@ -425,7 +422,7 @@ -- thread which is in a non-blocking mode. -- prop_catch_throwTo_masking_state_async :: forall m.-                                          ( MonadMaskingState m+                                          ( MonadMask  m                                           , MonadFork  m                                           , MonadSTM   m                                           , MonadDelay m@@ -454,7 +451,7 @@ -- 'willBlock' branch of 'ThrowTo' in 'schedule' is covered. -- prop_catch_throwTo_masking_state_async_mayblock :: forall m.-                                                ( MonadMaskingState m+                                                ( MonadMask  m                                                 , MonadFork  m                                                 , MonadSTM   m                                                 , MonadDelay m