@@ -69,6 +69,7 @@ import Deque.Strict (Deque)
69
69
import qualified Deque.Strict as Deque
70
70
71
71
import GHC.Exts (fromList )
72
+ import GHC.Conc (ThreadStatus (.. ), BlockReason (.. ))
72
73
73
74
import Control.Exception (NonTermination (.. ), assert , throw )
74
75
import Control.Monad (join )
@@ -122,14 +123,15 @@ labelledThreads threadMap =
122
123
--
123
124
data TimerVars s = TimerVars ! (TVar s TimeoutState ) ! (TVar s Bool )
124
125
125
-
126
126
-- | Internal state.
127
127
--
128
128
data SimState s a = SimState {
129
129
runqueue :: ! (Deque ThreadId ),
130
130
-- | All threads other than the currently running thread: both running
131
131
-- and blocked threads.
132
132
threads :: ! (Map ThreadId (Thread s a )),
133
+ -- | Keep track of the reason threads finished for 'threadStatus'
134
+ finished :: ! (Map ThreadId FinishedReason ),
133
135
-- | current time
134
136
curTime :: ! Time ,
135
137
-- | ordered list of timers
@@ -145,6 +147,7 @@ initialState =
145
147
SimState {
146
148
runqueue = mempty ,
147
149
threads = Map. empty,
150
+ finished = Map. empty,
148
151
curTime = Time 0 ,
149
152
timers = PSQ. empty,
150
153
clocks = Map. singleton (ClockId [] ) epoch1970,
@@ -189,6 +192,7 @@ schedule !thread@Thread{
189
192
! simstate@ SimState {
190
193
runqueue,
191
194
threads,
195
+ finished,
192
196
timers,
193
197
clocks,
194
198
nextVid, nextTmid,
@@ -207,9 +211,9 @@ schedule !thread@Thread{
207
211
208
212
ForkFrame -> do
209
213
-- this thread is done
210
- ! trace <- deschedule Terminated thread simstate
214
+ ! trace <- deschedule ( Terminated FinishedNormally ) thread simstate
211
215
return $ SimTrace time tid tlbl EventThreadFinished
212
- $ SimTrace time tid tlbl (EventDeschedule Terminated )
216
+ $ SimTrace time tid tlbl (EventDeschedule $ Terminated FinishedNormally )
213
217
$ trace
214
218
215
219
MaskFrame k maskst' ctl' -> do
@@ -227,7 +231,7 @@ schedule !thread@Thread{
227
231
let thread' = thread { threadControl = ThreadControl (k x) ctl' }
228
232
schedule thread' simstate
229
233
230
- Throw e -> {-# SCC "schedule.Throw" #-}
234
+ Throw thrower e -> {-# SCC "schedule.Throw" #-}
231
235
case unwindControlStack e thread of
232
236
Right thread'@ Thread { threadMasking = maskst' } -> do
233
237
-- We found a suitable exception handler, continue with that
@@ -246,10 +250,12 @@ schedule !thread@Thread{
246
250
247
251
| otherwise -> do
248
252
-- An unhandled exception in any other thread terminates the thread
249
- ! trace <- deschedule Terminated thread simstate
253
+ let reason | ThrowSelf <- thrower = FinishedNormally
254
+ | otherwise = FinishedDied
255
+ ! trace <- deschedule (Terminated reason) thread simstate
250
256
return $ SimTrace time tid tlbl (EventThrow e)
251
257
$ SimTrace time tid tlbl (EventThreadUnhandled e)
252
- $ SimTrace time tid tlbl (EventDeschedule Terminated )
258
+ $ SimTrace time tid tlbl (EventDeschedule $ Terminated reason )
253
259
$ trace
254
260
255
261
Catch action' handler k ->
@@ -265,7 +271,7 @@ schedule !thread@Thread{
265
271
case mbWHNF of
266
272
Left e -> do
267
273
-- schedule this thread to immediately raise the exception
268
- let thread' = thread { threadControl = ThreadControl (Throw e) ctl }
274
+ let thread' = thread { threadControl = ThreadControl (Throw ThrowSelf e) ctl }
269
275
schedule thread' simstate
270
276
Right whnf -> do
271
277
-- continue with the resulting WHNF
@@ -465,7 +471,7 @@ schedule !thread@Thread{
465
471
466
472
StmTxAborted _read e -> do
467
473
-- schedule this thread to immediately raise the exception
468
- let thread' = thread { threadControl = ThreadControl (Throw e) ctl }
474
+ let thread' = thread { threadControl = ThreadControl (Throw ThrowSelf e) ctl }
469
475
! trace <- schedule thread' simstate
470
476
return $ SimTrace time tid tlbl (EventTxAborted Nothing ) trace
471
477
@@ -494,6 +500,19 @@ schedule !thread@Thread{
494
500
threads' = Map. adjust (\ t -> t { threadLabel = Just l }) tid' threads
495
501
schedule thread' simstate { threads = threads' }
496
502
503
+ ThreadStatus tid' k ->
504
+ {-# SCC "schedule.ThreadStatus" #-} do
505
+ let result | Just r <- Map. lookup tid' finished = reasonToStatus r
506
+ | Just t <- Map. lookup tid' threads = threadStatus t
507
+ | otherwise = error " The impossible happened - tried to loookup thread in state."
508
+ reasonToStatus FinishedNormally = ThreadFinished
509
+ reasonToStatus FinishedDied = ThreadDied
510
+ threadStatus t | threadBlocked t = ThreadBlocked BlockedOnOther
511
+ | otherwise = ThreadRunning
512
+
513
+ thread' = thread { threadControl = ThreadControl (k result) ctl }
514
+ schedule thread' simstate
515
+
497
516
GetMaskState k ->
498
517
{-# SCC "schedule.GetMaskState" #-} do
499
518
let thread' = thread { threadControl = ThreadControl (k maskst) ctl }
@@ -518,7 +537,7 @@ schedule !thread@Thread{
518
537
{-# SCC "schedule.ThrowTo" #-} do
519
538
-- Throw to ourself is equivalent to a synchronous throw,
520
539
-- and works irrespective of masking state since it does not block.
521
- let thread' = thread { threadControl = ThreadControl (Throw e) ctl }
540
+ let thread' = thread { threadControl = ThreadControl (Throw ThrowSelf e) ctl }
522
541
trace <- schedule thread' simstate
523
542
return (SimTrace time tid tlbl (EventThrowTo e tid) trace)
524
543
@@ -548,8 +567,11 @@ schedule !thread@Thread{
548
567
-- be resolved if the thread terminates or if it leaves the exception
549
568
-- handler (when restoring the masking state would trigger the any
550
569
-- new pending async exception).
551
- let adjustTarget t@ Thread { threadControl = ThreadControl _ ctl' } =
552
- t { threadControl = ThreadControl (Throw e) ctl'
570
+ let thrower = case threadMasking <$> Map. lookup tid' threads of
571
+ Just Unmasked -> ThrowOther
572
+ _ -> ThrowSelf
573
+ adjustTarget t@ Thread { threadControl = ThreadControl _ ctl' } =
574
+ t { threadControl = ThreadControl (Throw thrower e) ctl'
553
575
, threadBlocked = False
554
576
}
555
577
simstate'@ SimState { threads = threads' }
@@ -618,7 +640,7 @@ deschedule Interruptable !thread@Thread {
618
640
-- So immediately raise the exception and unblock the blocked thread
619
641
-- if possible.
620
642
{-# SCC "deschedule.Interruptable.Unmasked" #-}
621
- let thread' = thread { threadControl = ThreadControl (Throw e) ctl
643
+ let thread' = thread { threadControl = ThreadControl (Throw ThrowSelf e) ctl
622
644
, threadMasking = MaskedInterruptible
623
645
, threadThrowTo = etids }
624
646
(unblocked,
@@ -653,13 +675,16 @@ deschedule Blocked !thread !simstate@SimState{threads} =
653
675
threads' = Map. insert (threadId thread') thread' threads in
654
676
reschedule simstate { threads = threads' }
655
677
656
- deschedule Terminated ! thread ! simstate@ SimState { curTime = time, threads } =
678
+ deschedule ( Terminated reason) ! thread ! simstate@ SimState { curTime = time, threads } =
657
679
-- This thread is done. If there are other threads blocked in a
658
680
-- ThrowTo targeted at this thread then we can wake them up now.
659
681
{-# SCC "deschedule.Terminated" #-}
660
682
let ! wakeup = map (l_labelled . snd ) (reverse (threadThrowTo thread))
661
683
(unblocked,
662
- ! simstate') = unblockThreads wakeup simstate
684
+ ! simstate') = unblockThreads wakeup
685
+ simstate { finished = Map. insert (threadId thread)
686
+ reason
687
+ (finished simstate) }
663
688
in do
664
689
! trace <- reschedule simstate'
665
690
return $ traceMany
0 commit comments