Duncan Coutts pushed to branch wip/dcoutts/issue-26717 at Glasgow Haskell Compiler / GHC

Commits:

25 changed files:

Changes:

  • changelog.d/T26716
    1
    +section: rts
    
    2
    +synopsis: Fix a use-after-free bug in the poll I/O manager
    
    3
    +issues: #26716, #26717
    
    4
    +mrs: !15519
    
    5
    +description: {
    
    6
    +  Experimental work on ASAN support for GHC (MR !15168) revealed a
    
    7
    +  use-after-free bug when using the combination of the new poll I/O
    
    8
    +  manager with the compacting GC. The ultimate cause is that a TSO's
    
    9
    +  `block_info` (used by I/O managers and many other parts of the RTS)
    
    10
    +  is sometimes a GC pointer and sometimes not, but without a consistent
    
    11
    +  and easy-to-follow rule for when this is the case. The solution has
    
    12
    +  been to clean up and enforce that the TSO's `why_blocked` enumeration
    
    13
    +  is a proper tag for the `block_info`, and to use an encoding that
    
    14
    +  determines precisely when the `block_info` is a pointer or not.
    
    15
    +}

  • docs/users_guide/eventlog-formats.rst
    ... ... @@ -211,7 +211,7 @@ Thread and scheduling events
    211 211
           * 1: HeapOverflow
    
    212 212
           * 2: StackOverflow
    
    213 213
           * 3: ThreadYielding
    
    214
    -      * 4: ThreadBlocked
    
    214
    +      * 4: unused
    
    215 215
           * 5: ThreadFinished
    
    216 216
           * 6: ForeignCall
    
    217 217
           * 7: BlockedOnMVar
    
    ... ... @@ -237,6 +237,10 @@ Thread and scheduling events
    237 237
        these eventlog stop thread codes are now independent. We are nevertheless
    
    238 238
        left with some historical warts:
    
    239 239
     
    
    240
    +    * 4: this was previously documented as `ThreadBlocked`. This code was used
    
    241
    +      in GHC 6.12.x (the first GHC version with eventlog support) and in 7.0.x.
    
    242
    +      From GHC 7.2 onwards this code is no longer used. Whenever a thread
    
    243
    +      blocks, a more detailed `BlockedOn*` code is used instead.
    
    240 244
         * 14,15: these correspond to GHC internal status codes `BlockedOnGA` and
    
    241 245
           `BlockedOnGA_NoSend` that are no longer used (and may never have been
    
    242 246
           used by any released version of GHC).
    

  • libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs
    ... ... @@ -607,13 +607,16 @@ threadStatus (ThreadId t) = IO $ \s ->
    607 607
             -- NB. keep these in sync with rts/include/rts/Constants.h
    
    608 608
          mk_stat 0  = ThreadRunning
    
    609 609
          mk_stat 1  = ThreadBlocked BlockedOnMVar
    
    610
    -     mk_stat 2  = ThreadBlocked BlockedOnBlackHole
    
    611
    -     mk_stat 6  = ThreadBlocked BlockedOnSTM
    
    610
    +     mk_stat 2  = ThreadBlocked BlockedOnMVar -- BlockedOnMVarRead
    
    611
    +     mk_stat 3  = ThreadBlocked BlockedOnBlackHole
    
    612
    +     mk_stat 4  = ThreadBlocked BlockedOnException
    
    613
    +     -- 5,6,7: BlockedOn{Read,Write,Delay}
    
    614
    +     mk_stat 8  = ThreadBlocked BlockedOnSTM
    
    615
    +     mk_stat 9  = ThreadBlocked BlockedOnForeignCall
    
    612 616
          mk_stat 10 = ThreadBlocked BlockedOnForeignCall
    
    613
    -     mk_stat 11 = ThreadBlocked BlockedOnForeignCall
    
    614
    -     mk_stat 12 = ThreadBlocked BlockedOnException
    
    615
    -     mk_stat 14 = ThreadBlocked BlockedOnMVar -- possibly: BlockedOnMVarRead
    
    616
    -     -- NB. these are hardcoded in rts/PrimOps.cmm
    
    617
    +     -- 11: ThreadMigrating
    
    618
    +     -- 12: BlockedOnDoProc
    
    619
    +     -- 13,14,15: unused
    
    617 620
          mk_stat 16 = ThreadFinished
    
    618 621
          mk_stat 17 = ThreadDied
    
    619 622
          mk_stat _  = ThreadBlocked BlockedOnOther
    

  • rts/IOManager.c
    ... ... @@ -611,41 +611,6 @@ void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap)
    611 611
     }
    
    612 612
     
    
    613 613
     
    
    614
    -void scavengeTSOIOManager(StgTSO *tso)
    
    615
    -{
    
    616
    -    switch (iomgr_type) {
    
    617
    -
    
    618
    -            /* case IO_MANAGER_SELECT:
    
    619
    -             * BlockedOn{Read,Write} uses block_info.fd
    
    620
    -             * BlockedOnDelay        uses block_info.target
    
    621
    -             * both of these are not GC pointers, so there is nothing to do.
    
    622
    -             */
    
    623
    -
    
    624
    -#if defined(IOMGR_ENABLED_POLL)
    
    625
    -        case IO_MANAGER_POLL:
    
    626
    -            /* BlockedOn{Read,Write} uses block_info.aiop
    
    627
    -             * BlockedOnDelay        uses block_info.timeout
    
    628
    -             * both of these are heap allocated, so we can do the same in all
    
    629
    -             * cases, which is why we can use the generic block_info.closure.
    
    630
    -             */
    
    631
    -            evacuate(&tso->block_info.closure);
    
    632
    -            break;
    
    633
    -#endif
    
    634
    -
    
    635
    -            /* case IO_MANAGER_WIN32_LEGACY:
    
    636
    -             * BlockedOn{Read,Write,DoProc} uses block_info.async_reqID
    
    637
    -             * which is a plain integer, so nothing to scavenge.
    
    638
    -             */
    
    639
    -
    
    640
    -        default:
    
    641
    -            /* All the other I/O managers do not use I/O-related why_blocked
    
    642
    -             * reasons, so there are no cases to handle.
    
    643
    -             */
    
    644
    -            break;
    
    645
    -    }
    
    646
    -}
    
    647
    -
    
    648
    -
    
    649 614
     /* Declared in rts/IOInterface.h. Used only by the MIO threaded I/O manager on
    
    650 615
      * Unix platforms.
    
    651 616
      */
    
    ... ... @@ -807,16 +772,17 @@ bool syncIOWaitReady(Capability *cap,
    807 772
     #if defined(IOMGR_ENABLED_SELECT)
    
    808 773
             case IO_MANAGER_SELECT:
    
    809 774
             {
    
    810
    -            StgWord why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite;
    
    775
    +            StgThreadWhyBlocked why_blocked = (rw == IORead ? BlockedOnRead
    
    776
    +                                                            : BlockedOnWrite)
    
    777
    +                                            | BlockInfoForceNonClosure;
    
    811 778
                 tso->block_info.fd = fd;
    
    812
    -            RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    813 779
                 appendToIOBlockedQueue(cap, tso);
    
    780
    +            RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    814 781
                 return true;
    
    815 782
             }
    
    816 783
     #endif
    
    817 784
     #if defined(IOMGR_ENABLED_POLL)
    
    818 785
             case IO_MANAGER_POLL:
    
    819
    -            ASSERT(tso->why_blocked == NotBlocked);
    
    820 786
                 return syncIOWaitReadyPoll(cap, tso, rw, fd);
    
    821 787
     #endif
    
    822 788
             default:
    
    ... ... @@ -868,8 +834,8 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    868 834
             {
    
    869 835
                 LowResTime target = getDelayTarget(us_delay);
    
    870 836
                 tso->block_info.target = target;
    
    871
    -            RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
    
    872 837
                 insertIntoSleepingQueue(cap, tso, target);
    
    838
    +            RELEASE_STORE(&tso->why_blocked, BlockedOnDelay | BlockInfoForceNonClosure);
    
    873 839
                 return true;
    
    874 840
             }
    
    875 841
     #endif
    
    ... ... @@ -889,8 +855,8 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    889 855
                  * simplifies matters, so set the status to OnDoProc and put the
    
    890 856
                  * delayed thread on the blocked_queue.
    
    891 857
                  */
    
    892
    -            RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
    
    893 858
                 appendToIOBlockedQueue(cap, tso);
    
    859
    +            RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
    
    894 860
                 return true;
    
    895 861
             }
    
    896 862
     #endif
    
    ... ... @@ -906,6 +872,7 @@ void syncDelayCancel(Capability *cap, StgTSO *tso)
    906 872
         switch (iomgr_type) {
    
    907 873
     #if defined(IOMGR_ENABLED_SELECT)
    
    908 874
             case IO_MANAGER_SELECT:
    
    875
    +            ASSERT(tso->why_blocked == (BlockedOnDelay | BlockInfoForceNonClosure));
    
    909 876
                 removeThreadFromQueue(cap, &cap->iomgr->sleeping_queue, tso);
    
    910 877
                 break;
    
    911 878
     #endif
    

  • rts/IOManager.h
    ... ... @@ -291,11 +291,6 @@ void wakeupIOManager(void);
    291 291
     void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap);
    
    292 292
     
    
    293 293
     
    
    294
    -/* GC hook: scavenge I/O related tso->block_info. Used by scavengeTSO.
    
    295
    - */
    
    296
    -void scavengeTSOIOManager(StgTSO *tso);
    
    297
    -
    
    298
    -
    
    299 294
     /* Several code paths are almost identical between read and write paths. In
    
    300 295
      * such cases we use a shared code path with an enum to say which we're doing.
    
    301 296
      */
    

  • rts/PrimOps.cmm
    ... ... @@ -1145,12 +1145,12 @@ stg_threadStatuszh ( gcptr tso )
    1145 1145
         // contents of block_info too, then we'd have to do some synchronisation.
    
    1146 1146
     
    
    1147 1147
         if (what_next == ThreadComplete) {
    
    1148
    -        ret = 16;  // NB. magic, matches up with GHC.Conc.threadStatus
    
    1148
    +        ret = BlockedThreadComplete;  // NB. magic, matches up with GHC.Conc.threadStatus
    
    1149 1149
         } else {
    
    1150 1150
             if (what_next == ThreadKilled) {
    
    1151
    -            ret = 17;
    
    1151
    +            ret = BlockedThreadKilled;
    
    1152 1152
             } else {
    
    1153
    -            ret = why_blocked;
    
    1153
    +            ret = UntagWhyBlocked(why_blocked);
    
    1154 1154
             }
    
    1155 1155
         }
    
    1156 1156
     
    
    ... ... @@ -2313,7 +2313,8 @@ stg_asyncReadzh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2313 2313
         StgTSO_block_info(CurrentTSO) = reqID;
    
    2314 2314
     
    
    2315 2315
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2316
    -    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32;
    
    2316
    +    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32
    
    2317
    +                                            | BlockInfoForceNonClosure::I32;
    
    2317 2318
     
    
    2318 2319
         ccall appendToIOBlockedQueue(MyCapability() "ptr", CurrentTSO "ptr");
    
    2319 2320
         jump stg_block_async();
    
    ... ... @@ -2332,7 +2333,8 @@ stg_asyncWritezh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2332 2333
         StgTSO_block_info(CurrentTSO) = reqID;
    
    2333 2334
     
    
    2334 2335
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2335
    -    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32;
    
    2336
    +    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32
    
    2337
    +                                            | BlockInfoForceNonClosure::I32;
    
    2336 2338
     
    
    2337 2339
         ccall appendToIOBlockedQueue(MyCapability() "ptr", CurrentTSO "ptr");
    
    2338 2340
         jump stg_block_async();
    

  • rts/RaiseAsync.c
    ... ... @@ -233,7 +233,6 @@ throwTo (Capability *cap, // the Capability we hold
    233 233
     uint32_t
    
    234 234
     throwToMsg (Capability *cap, MessageThrowTo *msg)
    
    235 235
     {
    
    236
    -    StgWord status;
    
    237 236
         StgTSO *target = ACQUIRE_LOAD(&msg->target);
    
    238 237
         Capability *target_cap;
    
    239 238
     
    
    ... ... @@ -268,9 +267,9 @@ check_target:
    268 267
             return THROWTO_BLOCKED;
    
    269 268
         }
    
    270 269
     
    
    271
    -    status = ACQUIRE_LOAD(&target->why_blocked);
    
    270
    +    StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&target->why_blocked);
    
    272 271
     
    
    273
    -    switch (status) {
    
    272
    +    switch (UntagWhyBlocked(why_blocked)) {
    
    274 273
         case NotBlocked:
    
    275 274
         {
    
    276 275
             if ((target->flags & TSO_BLOCKEX) == 0) {
    
    ... ... @@ -354,7 +353,7 @@ check_target:
    354 353
             StgMVar *mvar;
    
    355 354
             StgInfoTable *info USED_IF_THREADS;
    
    356 355
     
    
    357
    -        mvar = (StgMVar *)target->block_info.closure;
    
    356
    +        mvar = target->block_info.mvar;
    
    358 357
     
    
    359 358
             // ASSUMPTION: tso->block_info must always point to a
    
    360 359
             // closure.  In the threaded RTS it does.
    
    ... ... @@ -370,9 +369,10 @@ check_target:
    370 369
     
    
    371 370
             // we have the MVar, let's check whether the thread
    
    372 371
             // is still blocked on the same MVar.
    
    373
    -        if ((target->why_blocked != BlockedOnMVar
    
    374
    -             && target->why_blocked != BlockedOnMVarRead)
    
    375
    -            || (StgMVar *)target->block_info.closure != mvar) {
    
    372
    +        StgThreadWhyBlocked why_blocked_still = ACQUIRE_LOAD(&target->why_blocked);
    
    373
    +        if ((   why_blocked_still != BlockedOnMVar
    
    374
    +             && why_blocked_still != BlockedOnMVarRead)
    
    375
    +            || target->block_info.mvar != mvar) {
    
    376 376
                 unlockClosure((StgClosure *)mvar, info);
    
    377 377
                 goto retry;
    
    378 378
             }
    
    ... ... @@ -490,7 +490,7 @@ check_target:
    490 490
             goto retry;
    
    491 491
     
    
    492 492
         default:
    
    493
    -        barf("throwTo: unrecognised why_blocked (%d)", target->why_blocked);
    
    493
    +        barf("throwTo: unrecognised why_blocked (%d)", why_blocked);
    
    494 494
         }
    
    495 495
         barf("throwTo");
    
    496 496
     }
    
    ... ... @@ -625,7 +625,7 @@ awakenBlockedExceptionQueue (Capability *cap, StgTSO *tso)
    625 625
     static void
    
    626 626
     removeFromMVarBlockedQueue (StgTSO *tso)
    
    627 627
     {
    
    628
    -    StgMVar *mvar = (StgMVar*)tso->block_info.closure;
    
    628
    +    StgMVar *mvar = tso->block_info.mvar;
    
    629 629
         StgMVarTSOQueue *q = (StgMVarTSOQueue*)tso->_link;
    
    630 630
     
    
    631 631
         if (q == (StgMVarTSOQueue*)END_TSO_QUEUE) {
    
    ... ... @@ -667,7 +667,7 @@ removeFromMVarBlockedQueue (StgTSO *tso)
    667 667
     static void
    
    668 668
     removeFromQueues(Capability *cap, StgTSO *tso)
    
    669 669
     {
    
    670
    -  switch (tso->why_blocked) {
    
    670
    +  switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    671 671
     
    
    672 672
       case NotBlocked:
    
    673 673
       case ThreadMigrating:
    
    ... ... @@ -721,8 +721,8 @@ removeFromQueues(Capability *cap, StgTSO *tso)
    721 721
       }
    
    722 722
     
    
    723 723
      done:
    
    724
    -  RELAXED_STORE(&tso->why_blocked, NotBlocked);
    
    725 724
       appendToRunQueue(cap, tso);
    
    725
    +  RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    726 726
     }
    
    727 727
     
    
    728 728
     /* -----------------------------------------------------------------------------
    
    ... ... @@ -1105,9 +1105,9 @@ done:
    1105 1105
         IF_DEBUG(sanity, checkTSO(tso));
    
    1106 1106
     
    
    1107 1107
         // wake it up
    
    1108
    -    if (tso->why_blocked != NotBlocked) {
    
    1109
    -        tso->why_blocked = NotBlocked;
    
    1108
    +    if (RELAXED_LOAD(&tso->why_blocked) != NotBlocked) {
    
    1110 1109
             appendToRunQueue(cap,tso);
    
    1110
    +        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    1111 1111
         }
    
    1112 1112
     
    
    1113 1113
         return tso;
    

  • rts/RaiseAsync.h
    ... ... @@ -56,7 +56,7 @@ void awakenBlockedExceptionQueue (Capability *cap, StgTSO *tso);
    56 56
     INLINE_HEADER int
    
    57 57
     interruptible(StgTSO *t)
    
    58 58
     {
    
    59
    -  switch (t->why_blocked) {
    
    59
    +  switch (UntagWhyBlocked(t->why_blocked)) {
    
    60 60
       case BlockedOnMVar:
    
    61 61
       case BlockedOnSTM:
    
    62 62
       case BlockedOnMVarRead:
    

  • rts/STM.c
    ... ... @@ -264,7 +264,7 @@ static StgBool cond_lock_tvar(Capability *cap,
    264 264
     
    
    265 265
     static void park_tso(StgTSO *tso) {
    
    266 266
       ASSERT(tso -> why_blocked == NotBlocked);
    
    267
    -  tso -> block_info.closure = (StgClosure *) END_TSO_QUEUE;
    
    267
    +  tso->block_info.unused = END_TSO_QUEUE;
    
    268 268
       RELEASE_STORE(&tso -> why_blocked, BlockedOnSTM);
    
    269 269
       TRACE("park_tso on tso=%p", tso);
    
    270 270
     }
    

  • rts/Schedule.c
    ... ... @@ -174,6 +174,9 @@ static void deleteAllThreads (void);
    174 174
     static void deleteThread_(StgTSO *tso);
    
    175 175
     #endif
    
    176 176
     
    
    177
    +static inline EventThreadStatus eventlogThreadStatus(StgThreadReturnCode ret_code);
    
    178
    +static inline EventThreadStatus eventlogThreadStatusBlocked(StgThreadWhyBlocked why_blocked);
    
    179
    +
    
    177 180
     /* ---------------------------------------------------------------------------
    
    178 181
        Main scheduling loop.
    
    179 182
     
    
    ... ... @@ -522,20 +525,21 @@ run_thread:
    522 525
     #endif
    
    523 526
     
    
    524 527
         if (ret == ThreadBlocked) {
    
    525
    -        uint16_t why_blocked = ACQUIRE_LOAD(&t->why_blocked);
    
    528
    +        StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&t->why_blocked);
    
    529
    +        EventThreadStatus status = eventlogThreadStatusBlocked(why_blocked);
    
    530
    +        StgWord32 status_detail = 0;
    
    526 531
             if (why_blocked == BlockedOnBlackHole) {
    
    527 532
                 StgTSO *owner = blackHoleOwner(t->block_info.bh->bh);
    
    528
    -            traceEventStopThread(cap, t, t->why_blocked + 6,
    
    529
    -                                 owner != NULL ? owner->id : 0);
    
    530
    -        } else {
    
    531
    -            traceEventStopThread(cap, t, t->why_blocked + 6, 0);
    
    533
    +            status_detail = owner != NULL ? owner->id : 0;
    
    532 534
             }
    
    535
    +        traceEventStopThread(cap, t, status, status_detail);
    
    533 536
         } else {
    
    537
    +        EventThreadStatus status = eventlogThreadStatus(ret);
    
    538
    +        StgWord32 status_detail = 0;
    
    534 539
             if (ret == StackOverflow) {
    
    535
    -          traceEventStopThread(cap, t, ret, t->tot_stack_size);
    
    536
    -        } else {
    
    537
    -          traceEventStopThread(cap, t, ret, 0);
    
    540
    +            status_detail = t->tot_stack_size;
    
    538 541
             }
    
    542
    +        traceEventStopThread(cap, t, status, status_detail);
    
    539 543
         }
    
    540 544
     
    
    541 545
         ASSERT_FULL_CAPABILITY_INVARIANTS(cap,task);
    
    ... ... @@ -1096,7 +1100,7 @@ schedulePostRunThread (Capability *cap, StgTSO *t)
    1096 1100
         //
    
    1097 1101
         // and a is never equal to b given a consistent view of memory.
    
    1098 1102
         //
    
    1099
    -    if (t -> trec != NO_TREC && t -> why_blocked == NotBlocked) {
    
    1103
    +    if (t -> trec != NO_TREC && RELAXED_LOAD(&t->why_blocked) == NotBlocked) {
    
    1100 1104
             if (!stmValidateNestOfTransactions(cap, t -> trec, true)) {
    
    1101 1105
                 debugTrace(DEBUG_sched | DEBUG_stm,
    
    1102 1106
                            "trec %p found wasting its time", t);
    
    ... ... @@ -2512,17 +2516,18 @@ suspendThread (StgRegTable *reg, bool interruptible)
    2512 2516
       task = cap->running_task;
    
    2513 2517
       tso = cap->r.rCurrentTSO;
    
    2514 2518
     
    
    2515
    -  traceEventStopThread(cap, tso, THREAD_SUSPENDED_FOREIGN_CALL, 0);
    
    2519
    +  traceEventStopThread(cap, tso, STOP_THREAD_ForeignCall, 0);
    
    2516 2520
     
    
    2517 2521
       // XXX this might not be necessary --SDM
    
    2518 2522
       RELAXED_STORE(&tso->what_next, ThreadRunGHC);
    
    2519 2523
     
    
    2520 2524
       threadPaused(cap,tso);
    
    2521 2525
     
    
    2526
    +  tso->block_info.unused = END_TSO_QUEUE;
    
    2522 2527
       if (interruptible) {
    
    2523
    -    tso->why_blocked = BlockedOnCCall_Interruptible;
    
    2528
    +    RELEASE_STORE(&tso->why_blocked, BlockedOnCCall_Interruptible);
    
    2524 2529
       } else {
    
    2525
    -    tso->why_blocked = BlockedOnCCall;
    
    2530
    +    RELEASE_STORE(&tso->why_blocked, BlockedOnCCall);
    
    2526 2531
       }
    
    2527 2532
     
    
    2528 2533
       // Hand back capability
    
    ... ... @@ -2580,16 +2585,25 @@ resumeThread (void *task_)
    2580 2585
         tso = incall->suspended_tso;
    
    2581 2586
         incall->suspended_tso = NULL;
    
    2582 2587
         incall->suspended_cap = NULL;
    
    2588
    +
    
    2589
    +    // we set why_blocked previously in suspendThread
    
    2590
    +    ASSERT(tso->why_blocked == BlockedOnCCall ||
    
    2591
    +           tso->why_blocked == BlockedOnCCall_Interruptible);
    
    2592
    +
    
    2583 2593
         // we will modify tso->_link
    
    2584 2594
         IF_NONMOVING_WRITE_BARRIER_ENABLED {
    
    2585 2595
             updateRemembSetPushClosure(cap, (StgClosure *)tso->_link);
    
    2586 2596
         }
    
    2587 2597
         tso->_link = END_TSO_QUEUE;
    
    2598
    +    // but no need to modify tso->block_info.prev as coincidentally
    
    2599
    +    // it has the value we want already (since in suspendThread we set
    
    2600
    +    // tso->block_info.unused to END_TSO_QUEUE for BlockedOnCCall).
    
    2601
    +    ASSERT(tso->block_info.prev == END_TSO_QUEUE);
    
    2588 2602
     
    
    2589 2603
         traceEventRunThread(cap, tso);
    
    2590 2604
     
    
    2591 2605
         /* Reset blocking status */
    
    2592
    -    tso->why_blocked  = NotBlocked;
    
    2606
    +    RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    2593 2607
     
    
    2594 2608
         if ((tso->flags & TSO_BLOCKEX) == 0) {
    
    2595 2609
             // avoid locking the TSO if we don't have to
    
    ... ... @@ -2941,8 +2955,9 @@ deleteThread (StgTSO *tso)
    2941 2955
         // The TSO must be on the run queue of the Capability we own, or
    
    2942 2956
         // we must own all Capabilities.
    
    2943 2957
     
    
    2944
    -    if (tso->why_blocked != BlockedOnCCall &&
    
    2945
    -        tso->why_blocked != BlockedOnCCall_Interruptible) {
    
    2958
    +    StgThreadWhyBlocked why_blocked = RELAXED_LOAD(&tso->why_blocked);
    
    2959
    +    if (why_blocked != BlockedOnCCall &&
    
    2960
    +        why_blocked != BlockedOnCCall_Interruptible) {
    
    2946 2961
             throwToSingleThreaded(tso->cap,tso,NULL);
    
    2947 2962
         }
    
    2948 2963
     }
    
    ... ... @@ -2953,10 +2968,12 @@ deleteThread_(StgTSO *tso)
    2953 2968
     { // for forkProcess only:
    
    2954 2969
       // like deleteThread(), but we delete threads in foreign calls, too.
    
    2955 2970
     
    
    2956
    -    if (tso->why_blocked == BlockedOnCCall ||
    
    2957
    -        tso->why_blocked == BlockedOnCCall_Interruptible) {
    
    2971
    +    StgThreadWhyBlocked why_blocked = RELAXED_LOAD(&tso->why_blocked);
    
    2972
    +    if (why_blocked == BlockedOnCCall ||
    
    2973
    +        why_blocked == BlockedOnCCall_Interruptible) {
    
    2958 2974
             tso->what_next = ThreadKilled;
    
    2959 2975
             appendToRunQueue(tso->cap, tso);
    
    2976
    +        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    2960 2977
         } else {
    
    2961 2978
             deleteThread(tso);
    
    2962 2979
         }
    
    ... ... @@ -3307,7 +3324,7 @@ resurrectThreads (StgTSO *threads)
    3307 3324
             // Wake up the thread on the Capability it was last on
    
    3308 3325
             cap = tso->cap;
    
    3309 3326
     
    
    3310
    -        switch (tso->why_blocked) {
    
    3327
    +        switch (UntagWhyBlocked(RELAXED_LOAD(&tso->why_blocked))) {
    
    3311 3328
             case BlockedOnMVar:
    
    3312 3329
             case BlockedOnMVarRead:
    
    3313 3330
                 /* Called by GC - sched_mutex lock is currently held. */
    
    ... ... @@ -3346,3 +3363,40 @@ void setAllocLimitKill(bool shouldKill, bool shouldHook)
    3346 3363
        allocLimitKill = shouldKill;
    
    3347 3364
        allocLimitRunHook = shouldHook;
    
    3348 3365
     }
    
    3366
    +
    
    3367
    +/* Map from the internal thread return codes and the tso->why_blocked values to
    
    3368
    + * the external eventlog STOP_THREAD status codes. See issue #9003 for what
    
    3369
    + * goes wrong if we do not handle this mapping in an intentional fashion.
    
    3370
    + *
    
    3371
    + * For the internal values see Constants.h
    
    3372
    + * For the external values see rts/include/rts/EventLogFormat.h and
    
    3373
    + * docs/users_guide/eventlog-formats.rst
    
    3374
    + */
    
    3375
    +static const unsigned char thread_stop_code[] = {
    
    3376
    +    [HeapOverflow]   = STOP_THREAD_HeapOverflow,
    
    3377
    +    [StackOverflow]  = STOP_THREAD_StackOverflow,
    
    3378
    +    [ThreadYielding] = STOP_THREAD_ThreadYielding,
    
    3379
    +    [ThreadFinished] = STOP_THREAD_ThreadFinished
    
    3380
    +};
    
    3381
    +
    
    3382
    +static const unsigned char thread_blocked_code[] = {
    
    3383
    +    [BlockedOnMVar]                = STOP_THREAD_BlockedOnMVar,
    
    3384
    +    [BlockedOnMVarRead]            = STOP_THREAD_BlockedOnMVarRead,
    
    3385
    +    [BlockedOnBlackHole]           = STOP_THREAD_BlockedOnBlackHole,
    
    3386
    +    [BlockedOnRead]                = STOP_THREAD_BlockedOnRead,
    
    3387
    +    [BlockedOnWrite]               = STOP_THREAD_BlockedOnWrite,
    
    3388
    +    [BlockedOnDelay]               = STOP_THREAD_BlockedOnDelay,
    
    3389
    +    [BlockedOnSTM]                 = STOP_THREAD_BlockedOnSTM,
    
    3390
    +    [BlockedOnDoProc]              = STOP_THREAD_BlockedOnDoProc,
    
    3391
    +    [BlockedOnMsgThrowTo]          = STOP_THREAD_BlockedOnMsgThrowTo,
    
    3392
    +};
    
    3393
    +
    
    3394
    +static inline EventThreadStatus eventlogThreadStatus(StgThreadReturnCode ret_code)
    
    3395
    +{
    
    3396
    +    return thread_stop_code[ret_code];
    
    3397
    +}
    
    3398
    +
    
    3399
    +static inline EventThreadStatus eventlogThreadStatusBlocked(StgThreadWhyBlocked why_blocked)
    
    3400
    +{
    
    3401
    +    return thread_blocked_code[UntagWhyBlocked(why_blocked)];
    
    3402
    +}

  • rts/Threads.c
    ... ... @@ -97,8 +97,8 @@ createThread(Capability *cap, W_ size)
    97 97
     
    
    98 98
         // Always start with the compiled code evaluator
    
    99 99
         tso->what_next = ThreadRunGHC;
    
    100
    -    tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
    
    101
    -    tso->why_blocked  = NotBlocked;
    
    100
    +    tso->block_info.prev = END_TSO_QUEUE;
    
    101
    +    tso->why_blocked = NotBlocked;
    
    102 102
         tso->blocked_exceptions = END_BLOCKED_EXCEPTIONS_QUEUE;
    
    103 103
         tso->bq = (StgBlockingQueue *)END_TSO_QUEUE;
    
    104 104
         tso->flags = 0;
    
    ... ... @@ -291,13 +291,12 @@ tryWakeupThread (Capability *cap, StgTSO *tso)
    291 291
         }
    
    292 292
     #endif
    
    293 293
     
    
    294
    -    switch (ACQUIRE_LOAD(&tso->why_blocked))
    
    294
    +    switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked)))
    
    295 295
         {
    
    296 296
         case BlockedOnMVar:
    
    297 297
         case BlockedOnMVarRead:
    
    298 298
         {
    
    299 299
             if (tso->_link == END_TSO_QUEUE) {
    
    300
    -            tso->block_info.closure = (StgClosure*)END_TSO_QUEUE;
    
    301 300
                 goto unblock;
    
    302 301
             } else {
    
    303 302
                 return;
    
    ... ... @@ -336,8 +335,8 @@ tryWakeupThread (Capability *cap, StgTSO *tso)
    336 335
     unblock:
    
    337 336
         // just run the thread now, if the BH is not really available,
    
    338 337
         // we'll block again.
    
    339
    -    tso->why_blocked = NotBlocked;
    
    340 338
         appendToRunQueue(cap,tso);
    
    339
    +    RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    341 340
     
    
    342 341
         // We used to set the context switch flag here, which would
    
    343 342
         // trigger a context switch a short time in the future (at the end
    
    ... ... @@ -368,7 +367,8 @@ migrateThread (Capability *from, StgTSO *tso, Capability *to)
    368 367
         traceEventMigrateThread (from, tso, to->no);
    
    369 368
         // ThreadMigrating tells the target cap that it needs to be added to
    
    370 369
         // the run queue when it receives the MSG_TRY_WAKEUP.
    
    371
    -    tso->why_blocked = ThreadMigrating;
    
    370
    +    tso->block_info.unused = END_TSO_QUEUE;
    
    371
    +    RELEASE_STORE(&tso->why_blocked, ThreadMigrating);
    
    372 372
         tso->cap = to;
    
    373 373
         tryWakeupThread(from, tso);
    
    374 374
     }
    
    ... ... @@ -879,9 +879,9 @@ loop:
    879 879
     
    
    880 880
         // save why_blocked here, because waking up the thread destroys
    
    881 881
         // this information
    
    882
    -    StgWord why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    882
    +    StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    883 883
         ASSERT(why_blocked == BlockedOnMVarRead || why_blocked == BlockedOnMVar);
    
    884
    -    ASSERT(tso->block_info.closure == (StgClosure*)mvar);
    
    884
    +    ASSERT(tso->block_info.mvar == mvar);
    
    885 885
     
    
    886 886
         // actually perform the takeMVar
    
    887 887
         StgStack* stack = tso->stackobj;
    
    ... ... @@ -952,7 +952,7 @@ end:
    952 952
     void
    
    953 953
     printThreadBlockage(StgTSO *tso)
    
    954 954
     {
    
    955
    -  switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    955
    +  switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    956 956
     #if defined(mingw32_HOST_OS)
    
    957 957
         case BlockedOnDoProc:
    
    958 958
         debugBelch("is blocked on proc (request: %" FMT_Word ")", tso->block_info.async_reqID);
    
    ... ... @@ -971,10 +971,10 @@ printThreadBlockage(StgTSO *tso)
    971 971
     #endif
    
    972 972
         break;
    
    973 973
       case BlockedOnMVar:
    
    974
    -    debugBelch("is blocked on an MVar @ %p", tso->block_info.closure);
    
    974
    +    debugBelch("is blocked on an MVar @ %p", tso->block_info.mvar);
    
    975 975
         break;
    
    976 976
       case BlockedOnMVarRead:
    
    977
    -    debugBelch("is blocked on atomic MVar read @ %p", tso->block_info.closure);
    
    977
    +    debugBelch("is blocked on atomic MVar read @ %p", tso->block_info.mvar);
    
    978 978
         break;
    
    979 979
         break;
    
    980 980
       case BlockedOnBlackHole:
    
    ... ... @@ -1049,7 +1049,7 @@ printAllThreads(void)
    1049 1049
       debugBelch("other threads:\n");
    
    1050 1050
       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
    
    1051 1051
         for (t = generations[g].threads; t != END_TSO_QUEUE; t = next) {
    
    1052
    -      if (t->why_blocked != NotBlocked) {
    
    1052
    +      if (RELAXED_LOAD(&t->why_blocked) != NotBlocked) {
    
    1053 1053
               printThreadStatus(t);
    
    1054 1054
           }
    
    1055 1055
           next = t->global_link;
    

  • rts/Trace.c
    ... ... @@ -169,24 +169,20 @@ static void tracePreface (void)
    169 169
     
    
    170 170
     #if defined(DEBUG)
    
    171 171
     static char *thread_stop_reasons[] = {
    
    172
    -    [HeapOverflow] = "heap overflow",
    
    173
    -    [StackOverflow] = "stack overflow",
    
    174
    -    [ThreadYielding] = "yielding",
    
    175
    -    [ThreadBlocked] = "blocked",
    
    176
    -    [ThreadFinished] = "finished",
    
    177
    -    [THREAD_SUSPENDED_FOREIGN_CALL] = "suspended while making a foreign call",
    
    178
    -    [6 + BlockedOnMVar]         = "blocked on an MVar",
    
    179
    -    [6 + BlockedOnMVarRead]     = "blocked on an atomic MVar read",
    
    180
    -    [6 + BlockedOnBlackHole]    = "blocked on a black hole",
    
    181
    -    [6 + BlockedOnRead]         = "blocked on a read operation",
    
    182
    -    [6 + BlockedOnWrite]        = "blocked on a write operation",
    
    183
    -    [6 + BlockedOnDelay]        = "blocked on a delay operation",
    
    184
    -    [6 + BlockedOnSTM]          = "blocked on STM",
    
    185
    -    [6 + BlockedOnDoProc]       = "blocked on asyncDoProc",
    
    186
    -    [6 + BlockedOnCCall]        = "blocked on a foreign call",
    
    187
    -    [6 + BlockedOnCCall_Interruptible] = "blocked on a foreign call (interruptible)",
    
    188
    -    [6 + BlockedOnMsgThrowTo]   =  "blocked on throwTo",
    
    189
    -    [6 + ThreadMigrating]       =  "migrating"
    
    172
    +    [STOP_THREAD_HeapOverflow]        = "heap overflow",
    
    173
    +    [STOP_THREAD_StackOverflow]       = "stack overflow",
    
    174
    +    [STOP_THREAD_ThreadYielding]      = "yielding",
    
    175
    +    [STOP_THREAD_ThreadFinished]      = "finished",
    
    176
    +    [STOP_THREAD_ForeignCall]         = "suspended while making a foreign call",
    
    177
    +    [STOP_THREAD_BlockedOnMVar]       = "blocked on an MVar",
    
    178
    +    [STOP_THREAD_BlockedOnMVarRead]   = "blocked on an atomic MVar read",
    
    179
    +    [STOP_THREAD_BlockedOnBlackHole]  = "blocked on a black hole",
    
    180
    +    [STOP_THREAD_BlockedOnRead]       = "blocked on a read operation",
    
    181
    +    [STOP_THREAD_BlockedOnWrite]      = "blocked on a write operation",
    
    182
    +    [STOP_THREAD_BlockedOnDelay]      = "blocked on a delay operation",
    
    183
    +    [STOP_THREAD_BlockedOnSTM]        = "blocked on STM",
    
    184
    +    [STOP_THREAD_BlockedOnDoProc]     = "blocked on asyncDoProc",
    
    185
    +    [STOP_THREAD_BlockedOnMsgThrowTo] = "blocked on throwTo"
    
    190 186
     };
    
    191 187
     #endif
    
    192 188
     
    
    ... ... @@ -230,10 +226,10 @@ static void traceSchedEvent_stderr (Capability *cap, EventTypeNum tag,
    230 226
             break;
    
    231 227
     
    
    232 228
         case EVENT_STOP_THREAD:     // (cap, thread, status)
    
    233
    -        if (info1 == 6 + BlockedOnBlackHole) {
    
    229
    +        if (info1 == STOP_THREAD_BlockedOnBlackHole) {
    
    234 230
                 debugBelch("cap %d: thread %" FMT_Word "[\"%.*s\"]" " stopped (blocked on black hole owned by thread %lu)\n",
    
    235 231
                            cap->no, (W_)tso->id, threadLabelLen, threadLabel, (long)info2);
    
    236
    -        } else if (info1 == StackOverflow) {
    
    232
    +        } else if (info1 == STOP_THREAD_StackOverflow) {
    
    237 233
                 debugBelch("cap %d: thread %" FMT_Word "[\"%.*s\"]"
    
    238 234
                            " stopped (stack overflow, size %lu)\n",
    
    239 235
                           cap->no, (W_)tso->id, threadLabelLen, threadLabel, (long)info2);
    

  • rts/Trace.h
    ... ... @@ -600,7 +600,7 @@ INLINE_HEADER void traceEventRunThread(Capability *cap STG_UNUSED,
    600 600
     
    
    601 601
     INLINE_HEADER void traceEventStopThread(Capability          *cap    STG_UNUSED,
    
    602 602
                                             StgTSO              *tso    STG_UNUSED,
    
    603
    -                                        StgThreadReturnCode  status STG_UNUSED,
    
    603
    +                                        EventThreadStatus    status STG_UNUSED,
    
    604 604
                                             StgWord32           info    STG_UNUSED)
    
    605 605
     {
    
    606 606
         traceSchedEvent2(cap, EVENT_STOP_THREAD, tso, status, info);
    

  • rts/TraverseHeap.c
    ... ... @@ -1242,15 +1242,12 @@ inner_loop:
    1242 1242
             traversePushClosure(ts, (StgClosure *) tso->blocked_exceptions, c, sep, child_data);
    
    1243 1243
             traversePushClosure(ts, (StgClosure *) tso->bq, c, sep, child_data);
    
    1244 1244
             traversePushClosure(ts, (StgClosure *) tso->trec, c, sep, child_data);
    
    1245
    -        switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    1246
    -        case BlockedOnMVar:
    
    1247
    -        case BlockedOnMVarRead:
    
    1248
    -        case BlockedOnBlackHole:
    
    1249
    -        case BlockedOnMsgThrowTo:
    
    1245
    +
    
    1246
    +        StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    1247
    +        if (IsBlockInfoClosure(why_blocked) && why_blocked != NotBlocked) {
    
    1248
    +            // The NotBlocked case uses block_info.prev as a TSO back link.
    
    1249
    +            // Do not follow in that case or we'll get into a loop.
    
    1250 1250
                 traversePushClosure(ts, tso->block_info.closure, c, sep, child_data);
    
    1251
    -            break;
    
    1252
    -        default:
    
    1253
    -            break;
    
    1254 1251
             }
    
    1255 1252
             goto loop;
    
    1256 1253
         }
    

  • rts/include/rts/Constants.h
    ... ... @@ -248,33 +248,83 @@
    248 248
     
    
    249 249
     /*
    
    250 250
      * Constants for the why_blocked field of a TSO
    
    251
    - * NB. keep these in sync with GHC/Conc/Sync.hs: threadStatus
    
    251
    + *
    
    252
    + * These say why the TSO is blocked, and also act as the tag for the
    
    253
    + * block_info union. The comment for each tag below says which member
    
    254
    + * of the block_info union is used.
    
    255
    + *
    
    256
    + * We also use the why_blocked to determine if the block_info contains
    
    257
    + * a closure or not. There are three classes of tag:
    
    258
    + * 1. why_blocked tags where block_info is always a closure;
    
    259
    + * 2. why_blocked tags where block_info is never a closure;
    
    260
    + * 3. why_blocked tags where block_info is sometimes a closure;
    
    261
    + *
    
    262
    + * We use the following encoding scheme for the three classes above:
    
    263
    + * 1. the tag value has bits 3 and 4 unset (values 0..7);
    
    264
    + * 2. the tag value has bit 3 set (values 8..15); and
    
    265
    + * 3. the tag value has bit 4 set when it is not a closure and unset
    
    266
    + *    when it is a closure.
    
    267
    + *
    
    268
    + * This scheme makes it cheap and simple to check if the GC needs to
    
    269
    + * look at the block_info.closure.
    
    270
    + *
    
    271
    + * The reason for the encoding using 2 marker bits rather than 1 is
    
    272
    + * that it minimises the cases in the code that need to use or check
    
    273
    + * the tag bits. The only tags in class 3 are BlockedOn{Read,Write
    
    274
    + * Delay,DoProc} which are used by in-RTS I/O managers, and the only
    
    275
    + * ones that need to use block_info members that are not a closure are
    
    276
    + * the legacy I/O managers select and win32-legacy. So when these I/O
    
    277
    + * managers are removed then we can simplify the encoding.
    
    252 278
      */
    
    253
    -#define NotBlocked          0
    
    254
    -#define BlockedOnMVar       1
    
    255
    -#define BlockedOnMVarRead   14 /* TODO: renumber me, see #9003 */
    
    256
    -#define BlockedOnBlackHole  2
    
    257
    -#define BlockedOnRead       3
    
    258
    -#define BlockedOnWrite      4
    
    259
    -#define BlockedOnDelay      5
    
    260
    -#define BlockedOnSTM        6
    
    261
    -
    
    262
    -/* Win32 only: */
    
    263
    -#define BlockedOnDoProc     7
    
    264
    -
    
    265
    -/* Only relevant for THREADED_RTS: */
    
    266
    -#define BlockedOnCCall      10
    
    267
    -#define BlockedOnCCall_Interruptible 11
    
    268
    -   /* same as above but permit killing the worker thread */
    
    269
    -
    
    270
    -/* Involved in a message sent to tso->msg_cap */
    
    271
    -#define BlockedOnMsgThrowTo 12
    
    279
    +#define BlockInfoForceNonClosure 16
    
    280
    +#define UntagWhyBlocked(why) ((why) & 15)
    
    281
    +#define IsBlockInfoClosure(why) (((why) & 24) == 0)
    
    282
    +/*
    
    283
    + * In the threaded RTS there is an invariant that the block_info union
    
    284
    + * is always a valid GC closure. To ensure this, the tags that use
    
    285
    + * block_info.unused, always set it to END_TSO_QUEUE. The non-closure
    
    286
    + * why_blocked tags are only used by I/O managers on the non-threaded
    
    287
    + * RTS. New in-RTS I/O managers use the AIOP and TimeoutQueue mechanism
    
    288
    + * which are closures.
    
    289
    + *
    
    290
    + * Note: keep these in sync with GHC/Conc/Sync.hs: threadStatus
    
    291
    + * Note: keep these in sync with Schedule.c: eventlogStopStatus which
    
    292
    + * converts the constants here to the ones used in the eventlog.
    
    293
    + */
    
    294
    +#define NotBlocked          0 /* Uses block_info.prev */
    
    295
    +#define BlockedOnMVar       1 /* Uses block_info.mvar */
    
    296
    +#define BlockedOnMVarRead   2 /* Uses block_info.mvar */
    
    297
    +#define BlockedOnBlackHole  3 /* Uses block_info.bh */
    
    298
    +#define BlockedOnMsgThrowTo 4 /* Uses block_info.throwto */
    
    299
    +#define BlockedOnRead       5 /* Uses block_info.aiop or uses .fd or
    
    300
    +                                 .async_result with BlockInfoForceNonClosure */
    
    301
    +#define BlockedOnWrite      6 /* Uses block_info.aiop or uses .fd or
    
    302
    +                                 .async_result with BlockInfoForceNonClosure */
    
    303
    +#define BlockedOnDelay      7 /* Uses block_info.timeout or
    
    304
    +                                 uses .target with BlockInfoForceNonClosure */
    
    305
    +
    
    306
    +#define BlockedOnSTM                  8 /* Uses block_info.unused */
    
    307
    +#define BlockedOnCCall                9 /* Uses block_info.unused */
    
    308
    +#define BlockedOnCCall_Interruptible 10 /* Uses block_info.unused
    
    309
    +                                        * Same as BlockedOnCCall but permits
    
    310
    +                                        * killing the worker thread */
    
    311
    +#define ThreadMigrating              11 /* Uses block_info.unused */
    
    312
    +#define BlockedOnDoProc              12 /* Uses block_info.async_result
    
    313
    +                                         * used by win32-legacy I/O manager */
    
    314
    +
    
    315
    +/* Reserved values, not values that why_blocked currently use. They
    
    316
    + * are used in primop stg_threadStatuszh and must not overlap with
    
    317
    + * other why_blocked status values. They could be changed, if the
    
    318
    + * threadStatus in ghc-internal is updated too.
    
    319
    + */
    
    320
    +#define BlockedThreadComplete 16
    
    321
    +#define BlockedThreadKilled   17
    
    272 322
     
    
    273
    -/* The thread is not on any run queues, but can be woken up
    
    274
    -   by tryWakeupThread() */
    
    275
    -#define ThreadMigrating     13
    
    323
    +/* Next available non-closure why_blocked tag numbers are: 13,14,15
    
    324
    + * For more closure tag numbers, shift up all the non-closure ones
    
    325
    + * and adjust the BlockInfoForceNonClosure tag and related macros.
    
    326
    + * If we reach BlockInfoForceNonClosure then shift that up. */
    
    276 327
     
    
    277
    -/* Next number is 15.  */
    
    278 328
     
    
    279 329
     /*
    
    280 330
      * These constants are returned to the scheduler by a thread that has
    
    ... ... @@ -286,6 +336,7 @@
    286 336
     #define ThreadYielding 3
    
    287 337
     #define ThreadBlocked  4
    
    288 338
     #define ThreadFinished 5
    
    339
    +/* If this is ever extended, also adjust the eventlogStopStatus mapping */
    
    289 340
     
    
    290 341
     /*
    
    291 342
      * Flags for the tso->flags field.
    

  • rts/include/rts/EventLogFormat.h
    ... ... @@ -26,7 +26,7 @@
    26 26
      *    - generate the event itself by calling postEvent() somewhere
    
    27 27
      *
    
    28 28
      *  - Describe the meaning and encoding of the event in the users guide
    
    29
    - *    (docs/user_guide/eventlog-formats.rst)
    
    29
    + *    (docs/users_guide/eventlog-formats.rst)
    
    30 30
      *
    
    31 31
      *  - In the Haskell code to parse the event log file:
    
    32 32
      *    - add types and code to read the new event
    
    ... ... @@ -85,27 +85,26 @@
    85 85
     
    
    86 86
     /*
    
    87 87
      * Status values for EVENT_STOP_THREAD
    
    88
    - *
    
    89
    - * 1-5 are the StgRun return values (from rts/include/Constants.h):
    
    90
    - *
    
    91
    - * #define HeapOverflow   1
    
    92
    - * #define StackOverflow  2
    
    93
    - * #define ThreadYielding 3
    
    94
    - * #define ThreadBlocked  4
    
    95
    - * #define ThreadFinished 5
    
    96
    - * #define ForeignCall                  6
    
    97
    - * #define BlockedOnMVar                7
    
    98
    - * #define BlockedOnBlackHole           8
    
    99
    - * #define BlockedOnRead                9
    
    100
    - * #define BlockedOnWrite               10
    
    101
    - * #define BlockedOnDelay               11
    
    102
    - * #define BlockedOnSTM                 12
    
    103
    - * #define BlockedOnDoProc              13
    
    104
    - * #define BlockedOnCCall               -- not used (see ForeignCall)
    
    105
    - * #define BlockedOnCCall_NoUnblockExc  -- not used (see ForeignCall)
    
    106
    - * #define BlockedOnMsgThrowTo          16
    
    88
    + * type EventThreadStatus
    
    89
    + * Keep values in sync with docs/users_guide/eventlog-formats.rst
    
    107 90
      */
    
    108
    -#define THREAD_SUSPENDED_FOREIGN_CALL 6
    
    91
    +#define STOP_THREAD_HeapOverflow        1
    
    92
    +#define STOP_THREAD_StackOverflow       2
    
    93
    +#define STOP_THREAD_ThreadYielding      3
    
    94
    +/* 4 unused */
    
    95
    +#define STOP_THREAD_ThreadFinished      5
    
    96
    +#define STOP_THREAD_ForeignCall         6
    
    97
    +#define STOP_THREAD_BlockedOnMVar       7
    
    98
    +#define STOP_THREAD_BlockedOnBlackHole  8
    
    99
    +#define STOP_THREAD_BlockedOnRead       9
    
    100
    +#define STOP_THREAD_BlockedOnWrite      10
    
    101
    +#define STOP_THREAD_BlockedOnDelay      11
    
    102
    +#define STOP_THREAD_BlockedOnSTM        12
    
    103
    +#define STOP_THREAD_BlockedOnDoProc     13
    
    104
    +/* 14-17 unused */
    
    105
    +#define STOP_THREAD_BlockedOnMsgThrowTo 18
    
    106
    +/* 19 unused */
    
    107
    +#define STOP_THREAD_BlockedOnMVarRead   20
    
    109 108
     
    
    110 109
     /*
    
    111 110
      * Capset type values for EVENT_CAPSET_CREATE
    

  • rts/include/rts/storage/TSO.h
    ... ... @@ -30,6 +30,15 @@ typedef StgWord64 StgThreadID;
    30 30
     
    
    31 31
     #define tsoLocked(tso) ((tso)->flags & TSO_LOCKED)
    
    32 32
     
    
    33
    +/* Type for the tso->why_blocked field. See values in Constants.h.
    
    34
    + *
    
    35
    + * The StgThreadWhyBlocked type could be 8-bits, but for reasons
    
    36
    + * unclear it is currently 32-bits. Previous comments here claimed
    
    37
    + * that the smallest atomic type on AArch64 is 32-bits, but this is
    
    38
    + * false.
    
    39
    + */
    
    40
    +typedef StgWord32 StgThreadWhyBlocked;
    
    41
    +
    
    33 42
     /*
    
    34 43
      * Type returned after running a thread.  Values of this type
    
    35 44
      * include HeapOverflow, StackOverflow etc.  See Constants.h for the
    
    ... ... @@ -37,20 +46,46 @@ typedef StgWord64 StgThreadID;
    37 46
      */
    
    38 47
     typedef unsigned int StgThreadReturnCode;
    
    39 48
     
    
    40
    -/* Reason for thread being blocked. See comment above struct StgTso_. */
    
    49
    +/* Additional information about how the thread is blocked.
    
    50
    + * The tso->why_blocked is the tag for this union. */
    
    41 51
     typedef union {
    
    52
    +  /* Used for generic read, for cases where block_info is a closure.
    
    53
    +   * Never used for writes. Use .unused below instead. */
    
    42 54
       StgClosure *closure;
    
    43
    -  StgTSO *prev; // a back-link when the TSO is on the run queue (NotBlocked)
    
    55
    +
    
    56
    +  /* For why_blocked cases where block_info is unused, this will be set to
    
    57
    +   * END_TSO_QUEUE, to maintain invariant that block_info.closure is valid */
    
    58
    +  StgTSO *unused;
    
    59
    +
    
    60
    +  /* case NotBlocked: A back-link when the TSO is on the run queue */
    
    61
    +  StgTSO *prev;
    
    62
    +
    
    63
    +  /* case BlockedOnMVar, BlockedOnMVarRead: the mvar the TSO is blocked on */
    
    64
    +  StgMVar *mvar;
    
    65
    +
    
    66
    +  /* case BlockedOnBlackHole */
    
    44 67
       struct MessageBlackHole_ *bh;
    
    68
    +
    
    69
    +  /* case BlockedOnMsgThrowTo */
    
    45 70
       struct MessageThrowTo_ *throwto;
    
    46
    -  struct MessageWakeup_  *wakeup;
    
    71
    +
    
    72
    +  /* case BlockedOnRead, BlockedOnWrite: legacy I/O managers */
    
    47 73
       StgInt fd;    /* StgInt instead of int, so that it's the same size as the ptrs */
    
    74
    +
    
    75
    +  /* case BlockedOnRead, BlockedOnWrite: new I/O managers */
    
    48 76
       StgAsyncIOOp *aiop;
    
    77
    +
    
    78
    +  /* case BlockedOnDelay: new I/O managers */
    
    49 79
       StgTimeoutQueue *timeout;
    
    80
    +
    
    50 81
     #if defined(mingw32_HOST_OS)
    
    82
    +  /* case BlockedOnRead, BlockedOnWrite, BlockedOnDoProc:
    
    83
    +   * used by the win32-legacy I/O manager */
    
    51 84
       StgWord async_reqID;
    
    52 85
     #endif
    
    86
    +
    
    53 87
     #if !defined(THREADED_RTS)
    
    88
    +  /* case BlockedOnDelay: used by the select I/O manager */
    
    54 89
       StgWord target;
    
    55 90
         // Only for the non-threaded RTS: the target time for a thread
    
    56 91
         // blocked in threadDelay, in units of 1ms.  This is a
    
    ... ... @@ -71,7 +106,21 @@ typedef union {
    71 106
      * have the reason in the why_blocked field of the TSO, and some
    
    72 107
      * further info (such as the closure the thread is blocked on, or the
    
    73 108
      * file descriptor if the thread is waiting on I/O) in the block_info
    
    74
    - * field.
    
    109
    + * field. See Constants.h for the why_blocked values.
    
    110
    + *
    
    111
    + * The why_blocked field must be updated atomically. The protocol for
    
    112
    + * updating block_info and why_blocked fields together is as follows:
    
    113
    + *
    
    114
    + *   Writes:
    
    115
    + *     - first write block_info (normal non-atomic write)
    
    116
    + *     - then write why_blocked with an atomic *store release*
    
    117
    + *
    
    118
    + *   Reads:
    
    119
    + *     - first read why_blocked with an atomic *load acquire*
    
    120
    + *     - then read block_info (normal non-atomic read)
    
    121
    + *
    
    122
    + *   Read of only why_blocked without block_info:
    
    123
    + *     - read why_blocked with an atomic *relaxed load*
    
    75 124
      */
    
    76 125
     
    
    77 126
     typedef struct StgTSO_ {
    
    ... ... @@ -121,11 +170,7 @@ typedef struct StgTSO_ {
    121 170
         StgWord16               what_next;      // Values defined in Constants.h
    
    122 171
         StgWord32               flags;          // Values defined in Constants.h
    
    123 172
     
    
    124
    -    /*
    
    125
    -     * N.B. why_blocked only has a handful of values but must be atomically
    
    126
    -     * updated; the smallest width which AArch64 supports for is 32-bits.
    
    127
    -     */
    
    128
    -    StgWord32               why_blocked;    // Values defined in Constants.h
    
    173
    +    StgThreadWhyBlocked     why_blocked;    // Values defined in Constants.h
    
    129 174
         StgTSOBlockInfo         block_info;     // Barrier provided by why_blocked
    
    130 175
         StgThreadID             id;
    
    131 176
         StgWord32               saved_errno;
    

  • rts/posix/Poll.c
    ... ... @@ -146,8 +146,9 @@ bool syncIOWaitReadyPoll(Capability *cap, StgTSO *tso,
    146 146
         aiop->notify.tso     = tso;
    
    147 147
         aiop->notify_type    = NotifyTSO;
    
    148 148
         aiop->live           = &stg_ASYNCIO_LIVE0_closure;
    
    149
    -    tso->why_blocked     = rw == IORead ? BlockedOnRead : BlockedOnWrite;
    
    150 149
         tso->block_info.aiop = aiop;
    
    150
    +    RELEASE_STORE(&tso->why_blocked, rw == IORead ? BlockedOnRead
    
    151
    +                                                  : BlockedOnWrite);
    
    151 152
         return asyncIOWaitReadyPoll(cap, aiop, rw, fd);
    
    152 153
     }
    
    153 154
     
    
    ... ... @@ -194,7 +195,6 @@ void syncIOCancelPoll(Capability *cap, StgTSO *tso)
    194 195
          * We don't put the TSO back on the run queue or change the why_blocked
    
    195 196
          * status, as that is done by removeFromQueues (in the throwTo* functions).
    
    196 197
          */
    
    197
    -    tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
    
    198 198
     }
    
    199 199
     
    
    200 200
     
    
    ... ... @@ -259,10 +259,9 @@ static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop)
    259 259
                      * cap because the tso was not on the run queue of any cap and
    
    260 260
                      * so is not subject to thread migration.
    
    261 261
                      */
    
    262
    -                StgTSO *tso      = aiop->notify.tso;
    
    263
    -                tso->why_blocked = NotBlocked;
    
    264
    -                tso->_link       = END_TSO_QUEUE;
    
    262
    +                StgTSO *tso = aiop->notify.tso;
    
    265 263
                     pushOnRunQueue(cap, tso);
    
    264
    +                RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    266 265
                 }
    
    267 266
                 break;
    
    268 267
             }
    

  • rts/posix/Select.c
    ... ... @@ -105,11 +105,10 @@ static bool wakeUpSleepingThreads (Capability *cap, LowResTime now)
    105 105
                 break;
    
    106 106
             }
    
    107 107
             iomgr->sleeping_queue = tso->_link;
    
    108
    -        RELAXED_STORE(&tso->why_blocked, NotBlocked);
    
    109
    -        tso->_link = END_TSO_QUEUE;
    
    110 108
             IF_DEBUG(scheduler, debugBelch("Waking up sleeping thread %"
    
    111 109
                                            FMT_StgThreadID "\n", tso->id));
    
    112 110
             pushOnRunQueue(cap,tso);
    
    111
    +        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    113 112
             flag = true;
    
    114 113
         }
    
    115 114
         return flag;
    
    ... ... @@ -268,7 +267,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait)
    268 267
            * So the (int) cast should be removed across the code base once
    
    269 268
            * GHC requires a version of FreeBSD that has that change in it.
    
    270 269
            */
    
    271
    -        switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    270
    +        switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    272 271
             case BlockedOnRead:
    
    273 272
               {
    
    274 273
                 int fd = tso->block_info.fd;
    
    ... ... @@ -397,7 +396,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait)
    397 396
                   int fd;
    
    398 397
                   enum FdState fd_state = RTS_FD_IS_BLOCKING;
    
    399 398
     
    
    400
    -              switch (tso->why_blocked) {
    
    399
    +              switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    401 400
                   case BlockedOnRead:
    
    402 401
                       fd = tso->block_info.fd;
    
    403 402
     
    
    ... ... @@ -436,9 +435,8 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait)
    436 435
                       IF_DEBUG(scheduler,
    
    437 436
                           debugBelch("Waking up blocked thread %" FMT_StgThreadID "\n",
    
    438 437
                                      tso->id));
    
    439
    -                  tso->why_blocked = NotBlocked;
    
    440
    -                  tso->_link = END_TSO_QUEUE;
    
    441 438
                       pushOnRunQueue(cap,tso);
    
    439
    +                  RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    442 440
                       break;
    
    443 441
                   case RTS_FD_IS_BLOCKING:
    
    444 442
                       if (prev == NULL)
    

  • rts/posix/Timeout.c
    ... ... @@ -48,8 +48,8 @@ bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay)
    48 48
         initElemTimeoutQueue(timeout, notify, NotifyTSO, cap->r.rCCCS);
    
    49 49
     
    
    50 50
         ASSERT(tso->why_blocked == NotBlocked);
    
    51
    -    tso->why_blocked = BlockedOnDelay;
    
    52 51
         tso->block_info.timeout = timeout;
    
    52
    +    RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
    
    53 53
     
    
    54 54
         insertTimeoutQueue(&cap->iomgr->timeout_queue, timeout, target);
    
    55 55
     
    
    ... ... @@ -67,8 +67,6 @@ void syncDelayCancelTimeout(Capability *cap, StgTSO *tso)
    67 67
     
    
    68 68
         deleteTimeoutQueue(&cap->iomgr->timeout_queue, timeout);
    
    69 69
     
    
    70
    -    tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
    
    71
    -
    
    72 70
         /* the timeout is no longer accessible from anywhere (except here) */
    
    73 71
         IF_NONMOVING_WRITE_BARRIER_ENABLED {
    
    74 72
             updateRemembSetPushClosure(cap, (StgClosure *)timeout);
    
    ... ... @@ -120,10 +118,10 @@ static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout)
    120 118
         switch (timeout->notify_type) {
    
    121 119
             case NotifyTSO:
    
    122 120
             {
    
    123
    -            StgTSO *tso      = timeout->notify.tso;
    
    124
    -            tso->why_blocked = NotBlocked;
    
    125
    -            tso->_link       = END_TSO_QUEUE;
    
    121
    +            StgTSO *tso = timeout->notify.tso;
    
    122
    +            tso->_link  = END_TSO_QUEUE;
    
    126 123
                 pushOnRunQueue(cap, tso);
    
    124
    +            RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    127 125
                 break;
    
    128 126
             }
    
    129 127
             case NotifyMVar:
    

  • rts/sm/Compact.c
    ... ... @@ -468,16 +468,10 @@ thread_TSO (StgTSO *tso)
    468 468
         thread_(&tso->_link);
    
    469 469
         thread_(&tso->global_link);
    
    470 470
     
    
    471
    -    switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    472
    -    case BlockedOnMVar:
    
    473
    -    case BlockedOnMVarRead:
    
    474
    -    case BlockedOnBlackHole:
    
    475
    -    case BlockedOnMsgThrowTo:
    
    476
    -    case NotBlocked:
    
    471
    +    if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    472
    +        /* This also follows the block_info.prev back-link in
    
    473
    +         * the NotBlocked case, which may not be necessary. */
    
    477 474
             thread_(&tso->block_info.closure);
    
    478
    -        break;
    
    479
    -    default:
    
    480
    -        break;
    
    481 475
         }
    
    482 476
         thread_(&tso->blocked_exceptions);
    
    483 477
         thread_(&tso->bq);
    

  • rts/sm/NonMovingMark.c
    ... ... @@ -1055,16 +1055,10 @@ trace_tso (MarkQueue *queue, StgTSO *tso)
    1055 1055
         if (tso->label != NULL) {
    
    1056 1056
             markQueuePushClosure_(queue, (StgClosure *) tso->label);
    
    1057 1057
         }
    
    1058
    -    switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    1059
    -    case BlockedOnMVar:
    
    1060
    -    case BlockedOnMVarRead:
    
    1061
    -    case BlockedOnBlackHole:
    
    1062
    -    case BlockedOnMsgThrowTo:
    
    1063
    -    case NotBlocked:
    
    1058
    +    if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    1059
    +        /* This also follows the block_info.prev back-link in
    
    1060
    +         * the NotBlocked case, which may not be necessary. */
    
    1064 1061
             markQueuePushClosure_(queue, tso->block_info.closure);
    
    1065
    -        break;
    
    1066
    -    default:
    
    1067
    -        break;
    
    1068 1062
         }
    
    1069 1063
     }
    
    1070 1064
     
    

  • rts/sm/Sanity.c
    ... ... @@ -779,13 +779,45 @@ checkTSO(StgTSO *tso)
    779 779
                info == &stg_WHITEHOLE_info); // used to happen due to STM doing
    
    780 780
                                              // lockTSO(), might not happen now
    
    781 781
     
    
    782
    -    if (   tso->why_blocked == BlockedOnMVar
    
    783
    -        || tso->why_blocked == BlockedOnMVarRead
    
    784
    -        || tso->why_blocked == BlockedOnBlackHole
    
    785
    -        || tso->why_blocked == BlockedOnMsgThrowTo
    
    786
    -        || tso->why_blocked == NotBlocked
    
    787
    -        ) {
    
    782
    +    StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    783
    +    switch (why_blocked) {
    
    784
    +    case NotBlocked:
    
    785
    +    case BlockedOnMVar:
    
    786
    +    case BlockedOnMVarRead:
    
    787
    +    case BlockedOnBlackHole:
    
    788
    +    case BlockedOnMsgThrowTo:
    
    789
    +    case BlockedOnRead:
    
    790
    +    case BlockedOnWrite:
    
    791
    +    case BlockedOnDelay:
    
    792
    +        //TODO: we could be more specific and check BlockedOnMVar has an MVar,
    
    793
    +        // BlockedOnBlackHole has a message, BlockedOnRead has an AIOP etc.
    
    794
    +        ASSERT(IsBlockInfoClosure(why_blocked));
    
    788 795
             ASSERT(LOOKS_LIKE_CLOSURE_PTR(tso->block_info.closure));
    
    796
    +        break;
    
    797
    +
    
    798
    +    case BlockedOnSTM:
    
    799
    +    case BlockedOnCCall:
    
    800
    +    case BlockedOnCCall_Interruptible:
    
    801
    +    case ThreadMigrating:
    
    802
    +#if defined(mingw32_HOST_OS) && !defined(THREADED_RTS)
    
    803
    +    case BlockedOnDoProc:
    
    804
    +#endif
    
    805
    +        ASSERT(!IsBlockInfoClosure(why_blocked));
    
    806
    +        ASSERT(tso->block_info.unused == END_TSO_QUEUE);
    
    807
    +        break;
    
    808
    +
    
    809
    +#if !defined(THREADED_RTS)
    
    810
    +    // Only these three can use BlockInfoForceNonClosure
    
    811
    +    case BlockedOnRead  | BlockInfoForceNonClosure:
    
    812
    +    case BlockedOnWrite | BlockInfoForceNonClosure:
    
    813
    +    case BlockedOnDelay | BlockInfoForceNonClosure:
    
    814
    +        ASSERT(!IsBlockInfoClosure(why_blocked));
    
    815
    +        break;
    
    816
    +#endif
    
    817
    +
    
    818
    +    default:
    
    819
    +        barf("checkTSO: strange tso->why_blocked: %d for TSO %"
    
    820
    +             FMT_StgThreadID " (%p)", why_blocked, tso->id, tso);
    
    789 821
         }
    
    790 822
     
    
    791 823
         ASSERT(LOOKS_LIKE_CLOSURE_PTR(tso->bq));
    

  • rts/sm/Scav.c
    ... ... @@ -138,29 +138,16 @@ scavengeTSO (StgTSO *tso)
    138 138
             evacuate((StgClosure **)&tso->label);
    
    139 139
         }
    
    140 140
     
    
    141
    -    switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    142
    -    case BlockedOnMVar:
    
    143
    -    case BlockedOnMVarRead:
    
    144
    -    case BlockedOnBlackHole:
    
    145
    -    case BlockedOnMsgThrowTo:
    
    146
    -    case NotBlocked:
    
    141
    +    if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    147 142
             evacuate(&tso->block_info.closure);
    
    148
    -        break;
    
    149
    -    case BlockedOnRead:
    
    150
    -    case BlockedOnWrite:
    
    151
    -    case BlockedOnDelay:
    
    152
    -    case BlockedOnDoProc:
    
    153
    -        scavengeTSOIOManager(tso);
    
    154
    -        break;
    
    155
    -    default:
    
    143
    +    } else {
    
    156 144
     #if defined(THREADED_RTS)
    
    157 145
         // in the THREADED_RTS, block_info.closure must always point to a
    
    158 146
         // valid closure, because we assume this in throwTo().  In the
    
    159 147
         // non-threaded RTS it might be a FD (for
    
    160 148
         // BlockedOnRead/BlockedOnWrite) or a time value (BlockedOnDelay)
    
    161
    -        tso->block_info.closure = (StgClosure *)END_TSO_QUEUE;
    
    149
    +        ASSERT(tso->block_info.unused == END_TSO_QUEUE);
    
    162 150
     #endif
    
    163
    -        break;
    
    164 151
         }
    
    165 152
     
    
    166 153
         tso->dirty = gct->failed_to_evac;
    

  • rts/win32/AsyncMIO.c
    ... ... @@ -298,7 +298,7 @@ start:
    298 298
                 for(tso = iomgr->blocked_queue_hd; tso != END_TSO_QUEUE;
    
    299 299
                       tso = tso->_link) {
    
    300 300
     
    
    301
    -                switch(ACQUIRE_LOAD(&tso->why_blocked)) {
    
    301
    +                switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    302 302
                     case BlockedOnRead:
    
    303 303
                     case BlockedOnWrite:
    
    304 304
                     case BlockedOnDoProc:
    
    ... ... @@ -318,8 +318,6 @@ start:
    318 318
                             }
    
    319 319
     
    
    320 320
                             // Terminates the run queue + this inner for-loop.
    
    321
    -                        tso->_link = END_TSO_QUEUE;
    
    322
    -                        tso->why_blocked = NotBlocked;
    
    323 321
                             // For stg_block_async frames (read/write/doProc),
    
    324 322
                             // write len and errCode directly to the stack.
    
    325 323
                             // For stg_block_noregs frames (delay), nothing
    
    ... ... @@ -329,14 +327,14 @@ start:
    329 327
                                 tso->stackobj->sp[2] = (W_)errCode;
    
    330 328
                             }
    
    331 329
                             pushOnRunQueue(&MainCapability, tso);
    
    330
    +                        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    332 331
                             break;
    
    333 332
                         }
    
    334 333
                         break;
    
    335
    -                default:
    
    336
    -                    if (tso->why_blocked != NotBlocked) {
    
    337
    -                        barf("awaitRequests: odd thread state");
    
    338
    -                    }
    
    334
    +                case NotBlocked:
    
    339 335
                         break;
    
    336
    +                default:
    
    337
    +                    barf("awaitRequests: odd thread state");
    
    340 338
                     }
    
    341 339
     
    
    342 340
                     prev = tso;