@@ -119,10 +119,13 @@ labelledThreads threadMap =
119119 [] threadMap
120120
121121
122- -- | Timers mutable variables. First one supports 'newTimeout' api, the second
123- -- one 'registerDelay'.
122+ -- | Timers mutable variables. Supports 'newTimeout' api, the second
123+ -- one 'registerDelay', the third one 'threadDelay' .
124124--
125- data TimerVars s = TimerVars ! (TVar s TimeoutState ) ! (TVar s Bool )
125+ data TimerCompletionInfo s =
126+ Timer ! (TVar s TimeoutState )
127+ | TimerRegisterDelay ! (TVar s Bool )
128+ | TimerThreadDelay ! ThreadId
126129
127130-- | Internal state.
128131--
@@ -136,7 +139,7 @@ data SimState s a = SimState {
136139 -- | current time
137140 curTime :: ! Time ,
138141 -- | ordered list of timers
139- timers :: ! (OrdPSQ TimeoutId Time (TimerVars s )),
142+ timers :: ! (OrdPSQ TimeoutId Time (TimerCompletionInfo s )),
140143 -- | list of clocks
141144 clocks :: ! (Map ClockId UTCTime ),
142145 nextVid :: ! TVarId , -- ^ next unused 'TVarId'
@@ -346,30 +349,72 @@ schedule !thread@Thread{
346349 NewTimeout d k ->
347350 {-# SCC "schedule.NewTimeout.2" #-} do
348351 ! tvar <- execNewTVar nextVid
349- (Just $ " <<timeout-state " ++ show (unTimeoutId nextTmid) ++ " >>" )
350- TimeoutPending
351- ! tvar' <- execNewTVar (succ nextVid)
352- (Just $ " <<timeout " ++ show (unTimeoutId nextTmid) ++ " >>" )
353- False
352+ (Just $ " <<timeout-state " ++ show (unTimeoutId nextTmid) ++ " >>" )
353+ TimeoutPending
354354 let ! expiry = d `addTime` time
355- ! t = Timeout tvar tvar' nextTmid
356- ! timers' = PSQ. insert nextTmid expiry (TimerVars tvar tvar' ) timers
355+ ! t = Timeout tvar nextTmid
356+ ! timers' = PSQ. insert nextTmid expiry (Timer tvar) timers
357357 ! thread' = thread { threadControl = ThreadControl (k t) ctl }
358- ! trace <- schedule thread' simstate { timers = timers'
359- , nextVid = succ ( succ nextVid)
360- , nextTmid = succ nextTmid }
358+ trace <- schedule thread' simstate { timers = timers'
359+ , nextVid = succ nextVid
360+ , nextTmid = succ nextTmid }
361361 return (SimTrace time tid tlbl (EventTimerCreated nextTmid nextVid expiry) trace)
362362
363+ RegisterDelay d k | d < 0 ->
364+ {-# SCC "schedule.NewRegisterDelay" #-} do
365+ ! tvar <- execNewTVar nextVid
366+ (Just $ " <<timeout " ++ show (unTimeoutId nextTmid) ++ " >>" )
367+ True
368+ let ! expiry = d `addTime` time
369+ ! thread' = thread { threadControl = ThreadControl (k tvar) ctl }
370+ trace <- schedule thread' simstate { nextVid = succ nextVid }
371+ return (SimTrace time tid tlbl (EventRegisterDelayCreated nextTmid nextVid expiry) $
372+ SimTrace time tid tlbl (EventRegisterDelayFired nextTmid) $
373+ trace)
374+
375+ RegisterDelay d k ->
376+ {-# SCC "schedule.NewRegisterDelay" #-} do
377+ ! tvar <- execNewTVar nextVid
378+ (Just $ " <<timeout " ++ show (unTimeoutId nextTmid) ++ " >>" )
379+ False
380+ let ! expiry = d `addTime` time
381+ ! timers' = PSQ. insert nextTmid expiry (TimerRegisterDelay tvar) timers
382+ ! thread' = thread { threadControl = ThreadControl (k tvar) ctl }
383+ trace <- schedule thread' simstate { timers = timers'
384+ , nextVid = succ nextVid
385+ , nextTmid = succ nextTmid }
386+ return (SimTrace time tid tlbl
387+ (EventRegisterDelayCreated nextTmid nextVid expiry) trace)
388+
389+ ThreadDelay d k | d < 0 ->
390+ {-# SCC "schedule.NewThreadDelay" #-} do
391+ let ! expiry = d `addTime` time
392+ ! thread' = thread { threadControl = ThreadControl k ctl }
393+ trace <- schedule thread' simstate
394+ return (SimTrace time tid tlbl (EventThreadDelay expiry) $
395+ SimTrace time tid tlbl EventThreadDelayFired $
396+ trace)
397+
398+ ThreadDelay d k ->
399+ {-# SCC "schedule.NewThreadDelay" #-} do
400+ let ! expiry = d `addTime` time
401+ ! timers' = PSQ. insert nextTmid expiry (TimerThreadDelay tid) timers
402+ ! thread' = thread { threadControl = ThreadControl k ctl }
403+ ! trace <- deschedule Blocked thread' simstate { timers = timers'
404+ , nextTmid = succ nextTmid }
405+ return (SimTrace time tid tlbl (EventThreadDelay expiry) trace)
406+
407+
363408 -- we do not follow `GHC.Event` behaviour here; updating a timer to the past
364409 -- effectively cancels it.
365- UpdateTimeout (Timeout _tvar _tvar' tmid) d k | d < 0 ->
410+ UpdateTimeout (Timeout _tvar tmid) d k | d < 0 ->
366411 {-# SCC "schedule.UpdateTimeout" #-} do
367412 let ! timers' = PSQ. delete tmid timers
368413 ! thread' = thread { threadControl = ThreadControl k ctl }
369414 trace <- schedule thread' simstate { timers = timers' }
370415 return (SimTrace time tid tlbl (EventTimerCancelled tmid) trace)
371416
372- UpdateTimeout (Timeout _tvar _tvar' tmid) d k ->
417+ UpdateTimeout (Timeout _tvar tmid) d k ->
373418 {-# SCC "schedule.UpdateTimeout" #-} do
374419 -- updating an expired timeout is a noop, so it is safe
375420 -- to race using a timeout with updating or cancelling it
@@ -387,12 +432,12 @@ schedule !thread@Thread{
387432 let thread' = thread { threadControl = ThreadControl k ctl }
388433 schedule thread' simstate
389434
390- CancelTimeout (Timeout tvar _tvar' tmid) k ->
435+ CancelTimeout (Timeout tvar tmid) k ->
391436 {-# SCC "schedule.CancelTimeout" #-} do
392437 let ! timers' = PSQ. delete tmid timers
393438 ! thread' = thread { threadControl = ThreadControl k ctl }
394439 ! written <- execAtomically' (runSTM $ writeTVar tvar TimeoutCancelled )
395- (! wakeup, wokeby) <- threadsUnblockedByWrites written
440+ (wakeup, wokeby) <- threadsUnblockedByWrites written
396441 mapM_ (\ (SomeTVar var) -> unblockAllThreadsFromTVar var) written
397442 let (unblocked,
398443 simstate') = unblockThreads wakeup simstate
@@ -525,7 +570,7 @@ schedule !thread@Thread{
525570 (runIOSim action')
526571 (MaskFrame k maskst ctl)
527572 , threadMasking = maskst' }
528- ! trace <-
573+ trace <-
529574 case maskst' of
530575 -- If we're now unmasked then check for any pending async exceptions
531576 Unmasked -> SimTrace time tid tlbl (EventDeschedule Interruptable )
@@ -723,30 +768,43 @@ reschedule !simstate@SimState{ threads, timers, curTime = time } =
723768
724769 -- Reuse the STM functionality here to write all the timer TVars.
725770 -- Simplify to a special case that only reads and writes TVars.
726- ! written <- execAtomically' (runSTM $ mapM_ timeoutAction fired)
727- (wakeup , wokeby) <- threadsUnblockedByWrites written
771+ ! written <- execAtomically' (runSTM $ mapM_ timeoutSTMAction fired)
772+ (wakeupSTM , wokeby) <- threadsUnblockedByWrites written
728773 ! _ <- mapM_ (\ (SomeTVar tvar) -> unblockAllThreadsFromTVar tvar) written
729774
730- let (unblocked,
731- simstate') = unblockThreads wakeup simstate
775+ -- Check all fired threadDelays
776+ let wakeupThreadDelay = [ tid | TimerThreadDelay tid <- fired ]
777+ wakeup = wakeupThreadDelay ++ wakeupSTM
778+ (_, ! simstate') = unblockThreads wakeup simstate
779+
732780 ! trace <- reschedule simstate' { curTime = time'
733781 , timers = timers' }
734782 return $
735- traceMany ([ (time', ThreadId [- 1 ], Just " timer" , EventTimerExpired tmid)
736- | tmid <- tmids ]
783+ traceMany ([ ( time', ThreadId [- 1 ], Just " timer"
784+ , EventTimerFired tmid)
785+ | (tmid, Timer _) <- zip tmids fired ]
786+ ++ [ ( time', ThreadId [- 1 ], Just " register delay timer"
787+ , EventRegisterDelayFired tmid)
788+ | (tmid, TimerRegisterDelay _) <- zip tmids fired ]
737789 ++ [ (time', tid', tlbl', EventTxWakeup vids)
738- | tid' <- unblocked
790+ | tid' <- wakeupSTM
739791 , let tlbl' = lookupThreadLabel tid' threads
740- , let Just vids = Set. toList <$> Map. lookup tid' wokeby ])
792+ , let Just vids = Set. toList <$> Map. lookup tid' wokeby ]
793+ ++ [ ( time', tid, Just " thread delay timer"
794+ , EventThreadDelayFired )
795+ | tid <- wakeupThreadDelay ])
741796 trace
742797 where
743- timeoutAction ( TimerVars var bvar ) = do
798+ timeoutSTMAction ( Timer var) = do
744799 x <- readTVar var
745800 case x of
746- TimeoutPending -> writeTVar var TimeoutFired
747- >> writeTVar bvar True
801+ TimeoutPending -> writeTVar var TimeoutFired
748802 TimeoutFired -> error " MonadTimer(Sim): invariant violation"
749803 TimeoutCancelled -> return ()
804+ timeoutSTMAction (TimerRegisterDelay var) = writeTVar var True
805+ -- Note that 'threadDelay' is not handled via STM style wakeup, but rather
806+ -- it's handled directly above with 'wakeupThreadDelay' and 'unblockThreads'
807+ timeoutSTMAction (TimerThreadDelay _) = return ()
750808
751809unblockThreads :: [ThreadId ] -> SimState s a -> ([ThreadId ], SimState s a )
752810unblockThreads ! wakeup ! simstate@ SimState {runqueue, threads} =
0 commit comments