@@ -119,10 +119,13 @@ labelledThreads threadMap =
119
119
[] threadMap
120
120
121
121
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' .
124
124
--
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
126
129
127
130
-- | Internal state.
128
131
--
@@ -136,7 +139,7 @@ data SimState s a = SimState {
136
139
-- | current time
137
140
curTime :: ! Time ,
138
141
-- | ordered list of timers
139
- timers :: ! (OrdPSQ TimeoutId Time (TimerVars s )),
142
+ timers :: ! (OrdPSQ TimeoutId Time (TimerCompletionInfo s )),
140
143
-- | list of clocks
141
144
clocks :: ! (Map ClockId UTCTime ),
142
145
nextVid :: ! TVarId , -- ^ next unused 'TVarId'
@@ -346,30 +349,72 @@ schedule !thread@Thread{
346
349
NewTimeout d k ->
347
350
{-# SCC "schedule.NewTimeout.2" #-} do
348
351
! 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
354
354
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
357
357
! 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 }
361
361
return (SimTrace time tid tlbl (EventTimerCreated nextTmid nextVid expiry) trace)
362
362
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
+
363
408
-- we do not follow `GHC.Event` behaviour here; updating a timer to the past
364
409
-- effectively cancels it.
365
- UpdateTimeout (Timeout _tvar _tvar' tmid) d k | d < 0 ->
410
+ UpdateTimeout (Timeout _tvar tmid) d k | d < 0 ->
366
411
{-# SCC "schedule.UpdateTimeout" #-} do
367
412
let ! timers' = PSQ. delete tmid timers
368
413
! thread' = thread { threadControl = ThreadControl k ctl }
369
414
trace <- schedule thread' simstate { timers = timers' }
370
415
return (SimTrace time tid tlbl (EventTimerCancelled tmid) trace)
371
416
372
- UpdateTimeout (Timeout _tvar _tvar' tmid) d k ->
417
+ UpdateTimeout (Timeout _tvar tmid) d k ->
373
418
{-# SCC "schedule.UpdateTimeout" #-} do
374
419
-- updating an expired timeout is a noop, so it is safe
375
420
-- to race using a timeout with updating or cancelling it
@@ -387,12 +432,12 @@ schedule !thread@Thread{
387
432
let thread' = thread { threadControl = ThreadControl k ctl }
388
433
schedule thread' simstate
389
434
390
- CancelTimeout (Timeout tvar _tvar' tmid) k ->
435
+ CancelTimeout (Timeout tvar tmid) k ->
391
436
{-# SCC "schedule.CancelTimeout" #-} do
392
437
let ! timers' = PSQ. delete tmid timers
393
438
! thread' = thread { threadControl = ThreadControl k ctl }
394
439
! written <- execAtomically' (runSTM $ writeTVar tvar TimeoutCancelled )
395
- (! wakeup, wokeby) <- threadsUnblockedByWrites written
440
+ (wakeup, wokeby) <- threadsUnblockedByWrites written
396
441
mapM_ (\ (SomeTVar var) -> unblockAllThreadsFromTVar var) written
397
442
let (unblocked,
398
443
simstate') = unblockThreads wakeup simstate
@@ -525,7 +570,7 @@ schedule !thread@Thread{
525
570
(runIOSim action')
526
571
(MaskFrame k maskst ctl)
527
572
, threadMasking = maskst' }
528
- ! trace <-
573
+ trace <-
529
574
case maskst' of
530
575
-- If we're now unmasked then check for any pending async exceptions
531
576
Unmasked -> SimTrace time tid tlbl (EventDeschedule Interruptable )
@@ -723,30 +768,43 @@ reschedule !simstate@SimState{ threads, timers, curTime = time } =
723
768
724
769
-- Reuse the STM functionality here to write all the timer TVars.
725
770
-- 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
728
773
! _ <- mapM_ (\ (SomeTVar tvar) -> unblockAllThreadsFromTVar tvar) written
729
774
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
+
732
780
! trace <- reschedule simstate' { curTime = time'
733
781
, timers = timers' }
734
782
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 ]
737
789
++ [ (time', tid', tlbl', EventTxWakeup vids)
738
- | tid' <- unblocked
790
+ | tid' <- wakeupSTM
739
791
, 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 ])
741
796
trace
742
797
where
743
- timeoutAction ( TimerVars var bvar ) = do
798
+ timeoutSTMAction ( Timer var) = do
744
799
x <- readTVar var
745
800
case x of
746
- TimeoutPending -> writeTVar var TimeoutFired
747
- >> writeTVar bvar True
801
+ TimeoutPending -> writeTVar var TimeoutFired
748
802
TimeoutFired -> error " MonadTimer(Sim): invariant violation"
749
803
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 ()
750
808
751
809
unblockThreads :: [ThreadId ] -> SimState s a -> ([ThreadId ], SimState s a )
752
810
unblockThreads ! wakeup ! simstate@ SimState {runqueue, threads} =
0 commit comments