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

Commits:

20 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
    +}

  • rts/IOManager.c
    ... ... @@ -589,41 +589,6 @@ void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr)
    589 589
     }
    
    590 590
     
    
    591 591
     
    
    592
    -void scavengeTSOIOManager(StgTSO *tso)
    
    593
    -{
    
    594
    -    switch (iomgr_type) {
    
    595
    -
    
    596
    -            /* case IO_MANAGER_SELECT:
    
    597
    -             * BlockedOn{Read,Write} uses block_info.fd
    
    598
    -             * BlockedOnDelay        uses block_info.target
    
    599
    -             * both of these are not GC pointers, so there is nothing to do.
    
    600
    -             */
    
    601
    -
    
    602
    -#if defined(IOMGR_ENABLED_POLL)
    
    603
    -        case IO_MANAGER_POLL:
    
    604
    -            /* BlockedOn{Read,Write} uses block_info.aiop
    
    605
    -             * BlockedOnDelay        uses block_info.timeout
    
    606
    -             * both of these are heap allocated, so we can do the same in all
    
    607
    -             * cases, which is why we can use the generic block_info.closure.
    
    608
    -             */
    
    609
    -            evacuate(&tso->block_info.closure);
    
    610
    -            break;
    
    611
    -#endif
    
    612
    -
    
    613
    -            /* case IO_MANAGER_WIN32_LEGACY:
    
    614
    -             * BlockedOn{Read,Write,DoProc} uses block_info.async_reqID
    
    615
    -             * which is a plain integer, so nothing to scavenge.
    
    616
    -             */
    
    617
    -
    
    618
    -        default:
    
    619
    -            /* All the other I/O managers do not use I/O-related why_blocked
    
    620
    -             * reasons, so there are no cases to handle.
    
    621
    -             */
    
    622
    -            break;
    
    623
    -    }
    
    624
    -}
    
    625
    -
    
    626
    -
    
    627 592
     /* Declared in rts/IOInterface.h. Used only by the MIO threaded I/O manager on
    
    628 593
      * Unix platforms.
    
    629 594
      */
    
    ... ... @@ -824,16 +789,17 @@ bool syncIOWaitReady(CapIOManager *iomgr,
    824 789
     #if defined(IOMGR_ENABLED_SELECT)
    
    825 790
             case IO_MANAGER_SELECT:
    
    826 791
             {
    
    827
    -            StgWord why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite;
    
    792
    +            StgThreadWhyBlocked why_blocked = (rw == IORead ? BlockedOnRead
    
    793
    +                                                            : BlockedOnWrite)
    
    794
    +                                            | BlockInfoForceNonClosure;
    
    828 795
                 tso->block_info.fd = fd;
    
    829
    -            RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    830 796
                 appendToIOBlockedQueue(iomgr, tso);
    
    797
    +            RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    831 798
                 return true;
    
    832 799
             }
    
    833 800
     #endif
    
    834 801
     #if defined(IOMGR_ENABLED_POLL)
    
    835 802
             case IO_MANAGER_POLL:
    
    836
    -            ASSERT(tso->why_blocked == NotBlocked);
    
    837 803
                 return syncIOWaitReadyPoll(iomgr, tso, rw, fd);
    
    838 804
     #endif
    
    839 805
             default:
    
    ... ... @@ -890,8 +856,8 @@ bool syncDelay(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay)
    890 856
             {
    
    891 857
                 LowResTime target = getDelayTarget(us_delay);
    
    892 858
                 tso->block_info.target = target;
    
    893
    -            RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
    
    894 859
                 insertIntoSleepingQueue(iomgr, tso, target);
    
    860
    +            RELEASE_STORE(&tso->why_blocked, BlockedOnDelay | BlockInfoForceNonClosure);
    
    895 861
                 return true;
    
    896 862
             }
    
    897 863
     #endif
    
    ... ... @@ -911,8 +877,8 @@ bool syncDelay(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay)
    911 877
                  * simplifies matters, so set the status to OnDoProc and put the
    
    912 878
                  * delayed thread on the blocked_queue.
    
    913 879
                  */
    
    914
    -            RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
    
    915 880
                 appendToIOBlockedQueue(iomgr, tso);
    
    881
    +            RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
    
    916 882
                 return true;
    
    917 883
             }
    
    918 884
     #endif
    
    ... ... @@ -928,6 +894,7 @@ void syncDelayCancel(CapIOManager *iomgr, StgTSO *tso)
    928 894
         switch (iomgr_type) {
    
    929 895
     #if defined(IOMGR_ENABLED_SELECT)
    
    930 896
             case IO_MANAGER_SELECT:
    
    897
    +            ASSERT(tso->why_blocked == (BlockedOnDelay | BlockInfoForceNonClosure));
    
    931 898
                 removeThreadFromQueue(iomgr->cap, &iomgr->sleeping_queue, tso);
    
    932 899
                 break;
    
    933 900
     #endif
    

  • rts/IOManager.h
    ... ... @@ -311,11 +311,6 @@ void exitIOManager(bool wait_threads);
    311 311
     void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr);
    
    312 312
     
    
    313 313
     
    
    314
    -/* GC hook: scavenge I/O related tso->block_info. Used by scavengeTSO.
    
    315
    - */
    
    316
    -void scavengeTSOIOManager(StgTSO *tso);
    
    317
    -
    
    318
    -
    
    319 314
     /* Several code paths are almost identical between read and write paths. In
    
    320 315
      * such cases we use a shared code path with an enum to say which we're doing.
    
    321 316
      */
    

  • rts/Messages.c
    ... ... @@ -267,7 +267,8 @@ uint32_t messageBlackHole(Capability *cap, MessageBlackHole *msg)
    267 267
             // NB. we check to make sure that the owner is not the same as
    
    268 268
             // the current thread, since in that case it will not be on
    
    269 269
             // the run queue.
    
    270
    -        if (owner->why_blocked == NotBlocked && owner->id != msg->tso->id) {
    
    270
    +        if (RELAXED_LOAD(&owner->why_blocked) == NotBlocked &&
    
    271
    +            owner->id != msg->tso->id) {
    
    271 272
                 promoteInRunQueue(cap, owner);
    
    272 273
             }
    
    273 274
     
    
    ... ... @@ -328,7 +329,8 @@ uint32_t messageBlackHole(Capability *cap, MessageBlackHole *msg)
    328 329
                           msg->tso->id, owner->id);
    
    329 330
     
    
    330 331
             // See above, #3838
    
    331
    -        if (owner->why_blocked == NotBlocked && owner->id != msg->tso->id) {
    
    332
    +        if (RELAXED_LOAD(&owner->why_blocked) == NotBlocked &&
    
    333
    +            owner->id != msg->tso->id) {
    
    332 334
                 promoteInRunQueue(cap, owner);
    
    333 335
             }
    
    334 336
     
    

  • 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
     
    
    ... ... @@ -2319,7 +2319,8 @@ stg_asyncReadzh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2319 2319
         StgTSO_block_info(CurrentTSO) = reqID;
    
    2320 2320
     
    
    2321 2321
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2322
    -    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32;
    
    2322
    +    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32
    
    2323
    +                                            | BlockInfoForceNonClosure::I32;
    
    2323 2324
     
    
    2324 2325
         ccall appendToIOBlockedQueue(Capability_iomgr(MyCapability()) "ptr",
    
    2325 2326
                                      CurrentTSO "ptr");
    
    ... ... @@ -2339,7 +2340,8 @@ stg_asyncWritezh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2339 2340
         StgTSO_block_info(CurrentTSO) = reqID;
    
    2340 2341
     
    
    2341 2342
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2342
    -    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32;
    
    2343
    +    %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32
    
    2344
    +                                            | BlockInfoForceNonClosure::I32;
    
    2343 2345
     
    
    2344 2346
         ccall appendToIOBlockedQueue(Capability_iomgr(MyCapability()) "ptr",
    
    2345 2347
                                      CurrentTSO "ptr");
    

  • 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) {
    
    ... ... @@ -370,8 +369,9 @@ 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)
    
    372
    +        StgThreadWhyBlocked why_blocked_still = ACQUIRE_LOAD(&target->why_blocked);
    
    373
    +        if ((   why_blocked_still != BlockedOnMVar
    
    374
    +             && why_blocked_still != BlockedOnMVarRead)
    
    375 375
                 || target->block_info.mvar != mvar) {
    
    376 376
                 unlockClosure((StgClosure *)mvar, info);
    
    377 377
                 goto retry;
    
    ... ... @@ -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
     }
    
    ... ... @@ -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/Schedule.c
    ... ... @@ -181,7 +181,7 @@ static void truncateRunQueue(Capability *cap);
    181 181
     static StgTSO *popRunQueue (Capability *cap);
    
    182 182
     
    
    183 183
     static inline EventThreadStatus eventlogThreadStatus(StgThreadReturnCode ret_code);
    
    184
    -static inline EventThreadStatus eventlogThreadStatusBlocked(StgWord why_blocked);
    
    184
    +static inline EventThreadStatus eventlogThreadStatusBlocked(StgThreadWhyBlocked why_blocked);
    
    185 185
     
    
    186 186
     /* ---------------------------------------------------------------------------
    
    187 187
        Main scheduling loop.
    
    ... ... @@ -531,7 +531,7 @@ run_thread:
    531 531
     #endif
    
    532 532
     
    
    533 533
         if (ret == ThreadBlocked) {
    
    534
    -        uint16_t why_blocked = ACQUIRE_LOAD(&t->why_blocked);
    
    534
    +        StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&t->why_blocked);
    
    535 535
             EventThreadStatus status = eventlogThreadStatusBlocked(why_blocked);
    
    536 536
             StgWord32 status_detail = 0;
    
    537 537
             if (why_blocked == BlockedOnBlackHole) {
    
    ... ... @@ -1074,7 +1074,7 @@ schedulePostRunThread (Capability *cap, StgTSO *t)
    1074 1074
         //
    
    1075 1075
         // and a is never equal to b given a consistent view of memory.
    
    1076 1076
         //
    
    1077
    -    if (t -> trec != NO_TREC && t -> why_blocked == NotBlocked) {
    
    1077
    +    if (t -> trec != NO_TREC && RELAXED_LOAD(&t->why_blocked) == NotBlocked) {
    
    1078 1078
             if (!stmValidateNestOfTransactions(cap, t -> trec, true)) {
    
    1079 1079
                 debugTrace(DEBUG_sched | DEBUG_stm,
    
    1080 1080
                            "trec %p found wasting its time", t);
    
    ... ... @@ -2508,9 +2508,9 @@ suspendThread (StgRegTable *reg, bool interruptible)
    2508 2508
     
    
    2509 2509
       tso->block_info.unused = END_TSO_QUEUE;
    
    2510 2510
       if (interruptible) {
    
    2511
    -    tso->why_blocked = BlockedOnCCall_Interruptible;
    
    2511
    +    RELEASE_STORE(&tso->why_blocked, BlockedOnCCall_Interruptible);
    
    2512 2512
       } else {
    
    2513
    -    tso->why_blocked = BlockedOnCCall;
    
    2513
    +    RELEASE_STORE(&tso->why_blocked, BlockedOnCCall);
    
    2514 2514
       }
    
    2515 2515
     
    
    2516 2516
       // Hand back capability
    
    ... ... @@ -2568,16 +2568,25 @@ resumeThread (void *task_)
    2568 2568
         tso = incall->suspended_tso;
    
    2569 2569
         incall->suspended_tso = NULL;
    
    2570 2570
         incall->suspended_cap = NULL;
    
    2571
    +
    
    2572
    +    // we set why_blocked previously in suspendThread
    
    2573
    +    ASSERT(tso->why_blocked == BlockedOnCCall ||
    
    2574
    +           tso->why_blocked == BlockedOnCCall_Interruptible);
    
    2575
    +
    
    2571 2576
         // we will modify tso->_link
    
    2572 2577
         IF_NONMOVING_WRITE_BARRIER_ENABLED {
    
    2573 2578
             updateRemembSetPushClosure(cap, (StgClosure *)tso->_link);
    
    2574 2579
         }
    
    2575 2580
         tso->_link = END_TSO_QUEUE;
    
    2581
    +    // but no need to modify tso->block_info.prev as coincidentally
    
    2582
    +    // it has the value we want already (since in suspendThread we set
    
    2583
    +    // tso->block_info.unused to END_TSO_QUEUE for BlockedOnCCall).
    
    2584
    +    ASSERT(tso->block_info.prev == END_TSO_QUEUE);
    
    2576 2585
     
    
    2577 2586
         traceEventRunThread(cap, tso);
    
    2578 2587
     
    
    2579 2588
         /* Reset blocking status */
    
    2580
    -    tso->why_blocked  = NotBlocked;
    
    2589
    +    RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    2581 2590
     
    
    2582 2591
         if ((tso->flags & TSO_BLOCKEX) == 0) {
    
    2583 2592
             // avoid locking the TSO if we don't have to
    
    ... ... @@ -2950,8 +2959,9 @@ deleteThread (StgTSO *tso)
    2950 2959
         // The TSO must be on the run queue of the Capability we own, or
    
    2951 2960
         // we must own all Capabilities.
    
    2952 2961
     
    
    2953
    -    if (tso->why_blocked != BlockedOnCCall &&
    
    2954
    -        tso->why_blocked != BlockedOnCCall_Interruptible) {
    
    2962
    +    StgThreadWhyBlocked why_blocked = RELAXED_LOAD(&tso->why_blocked);
    
    2963
    +    if (why_blocked != BlockedOnCCall &&
    
    2964
    +        why_blocked != BlockedOnCCall_Interruptible) {
    
    2955 2965
             throwToSingleThreaded(tso->cap,tso,NULL);
    
    2956 2966
         }
    
    2957 2967
     }
    
    ... ... @@ -2962,10 +2972,12 @@ deleteThread_(StgTSO *tso)
    2962 2972
     { // for forkProcess only:
    
    2963 2973
       // like deleteThread(), but we delete threads in foreign calls, too.
    
    2964 2974
     
    
    2965
    -    if (tso->why_blocked == BlockedOnCCall ||
    
    2966
    -        tso->why_blocked == BlockedOnCCall_Interruptible) {
    
    2975
    +    StgThreadWhyBlocked why_blocked = RELAXED_LOAD(&tso->why_blocked);
    
    2976
    +    if (why_blocked == BlockedOnCCall ||
    
    2977
    +        why_blocked == BlockedOnCCall_Interruptible) {
    
    2967 2978
             tso->what_next = ThreadKilled;
    
    2968 2979
             appendToRunQueue(tso->cap, tso);
    
    2980
    +        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    2969 2981
         } else {
    
    2970 2982
             deleteThread(tso);
    
    2971 2983
         }
    
    ... ... @@ -3355,7 +3367,7 @@ resurrectThreads (StgTSO *threads)
    3355 3367
             // Wake up the thread on the Capability it was last on
    
    3356 3368
             cap = tso->cap;
    
    3357 3369
     
    
    3358
    -        switch (tso->why_blocked) {
    
    3370
    +        switch (UntagWhyBlocked(RELAXED_LOAD(&tso->why_blocked))) {
    
    3359 3371
             case BlockedOnMVar:
    
    3360 3372
             case BlockedOnMVarRead:
    
    3361 3373
                 /* Called by GC - sched_mutex lock is currently held. */
    
    ... ... @@ -3427,7 +3439,7 @@ static inline EventThreadStatus eventlogThreadStatus(StgThreadReturnCode ret_cod
    3427 3439
         return thread_stop_code[ret_code];
    
    3428 3440
     }
    
    3429 3441
     
    
    3430
    -static inline EventThreadStatus eventlogThreadStatusBlocked(StgWord why_blocked)
    
    3442
    +static inline EventThreadStatus eventlogThreadStatusBlocked(StgThreadWhyBlocked why_blocked)
    
    3431 3443
     {
    
    3432
    -    return thread_blocked_code[why_blocked];
    
    3444
    +    return thread_blocked_code[UntagWhyBlocked(why_blocked)];
    
    3433 3445
     }

  • rts/Threads.c
    ... ... @@ -291,7 +291,7 @@ 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:
    
    ... ... @@ -335,8 +335,8 @@ tryWakeupThread (Capability *cap, StgTSO *tso)
    335 335
     unblock:
    
    336 336
         // just run the thread now, if the BH is not really available,
    
    337 337
         // we'll block again.
    
    338
    -    tso->why_blocked = NotBlocked;
    
    339 338
         appendToRunQueue(cap,tso);
    
    339
    +    RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    340 340
     
    
    341 341
         // We used to set the context switch flag here, which would
    
    342 342
         // trigger a context switch a short time in the future (at the end
    
    ... ... @@ -368,7 +368,7 @@ migrateThread (Capability *from, StgTSO *tso, Capability *to)
    368 368
         // ThreadMigrating tells the target cap that it needs to be added to
    
    369 369
         // the run queue when it receives the MSG_TRY_WAKEUP.
    
    370 370
         tso->block_info.unused = END_TSO_QUEUE;
    
    371
    -    tso->why_blocked = ThreadMigrating;
    
    371
    +    RELEASE_STORE(&tso->why_blocked, ThreadMigrating);
    
    372 372
         tso->cap = to;
    
    373 373
         tryWakeupThread(from, tso);
    
    374 374
     }
    
    ... ... @@ -876,7 +876,7 @@ loop:
    876 876
     
    
    877 877
         // save why_blocked here, because waking up the thread destroys
    
    878 878
         // this information
    
    879
    -    StgWord why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    879
    +    StgThreadWhyBlocked why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
    
    880 880
         ASSERT(why_blocked == BlockedOnMVarRead || why_blocked == BlockedOnMVar);
    
    881 881
         ASSERT(tso->block_info.mvar == mvar);
    
    882 882
     
    
    ... ... @@ -949,7 +949,7 @@ end:
    949 949
     void
    
    950 950
     printThreadBlockage(StgTSO *tso)
    
    951 951
     {
    
    952
    -  switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    952
    +  switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    953 953
     #if defined(mingw32_HOST_OS)
    
    954 954
         case BlockedOnDoProc:
    
    955 955
         debugBelch("is blocked on proc (request: %" FMT_Word ")", tso->block_info.async_reqID);
    
    ... ... @@ -1046,7 +1046,7 @@ printAllThreads(void)
    1046 1046
       debugBelch("other threads:\n");
    
    1047 1047
       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
    
    1048 1048
         for (t = generations[g].threads; t != END_TSO_QUEUE; t = next) {
    
    1049
    -      if (t->why_blocked != NotBlocked) {
    
    1049
    +      if (RELAXED_LOAD(&t->why_blocked) != NotBlocked) {
    
    1050 1050
               printThreadStatus(t);
    
    1051 1051
           }
    
    1052 1052
           next = t->global_link;
    

  • 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
    ... ... @@ -253,6 +253,33 @@
    253 253
      * block_info union. The comment for each tag below says which member
    
    254 254
      * of the block_info union is used.
    
    255 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} which are used by in-RTS I/O managers, and the only ones that
    
    275
    + * need to use block_info members that are not a closure are the legacy
    
    276
    + * I/O managers select and win32-legacy. So when these I/O managers are
    
    277
    + * removed then we can simplify the encoding.
    
    278
    + */
    
    279
    +#define BlockInfoForceNonClosure 16
    
    280
    +#define UntagWhyBlocked(why) ((why) & 15)
    
    281
    +#define IsBlockInfoClosure(why) (((why) & 24) == 0)
    
    282
    +/*
    
    256 283
      * In the threaded RTS there is an invariant that the block_info union
    
    257 284
      * is always a valid GC closure. To ensure this, the tags that use
    
    258 285
      * block_info.unused, always set it to END_TSO_QUEUE. The non-closure
    
    ... ... @@ -269,21 +296,31 @@
    269 296
     #define BlockedOnMVarRead   2 /* Uses block_info.mvar */
    
    270 297
     #define BlockedOnBlackHole  3 /* Uses block_info.bh */
    
    271 298
     #define BlockedOnMsgThrowTo 4 /* Uses block_info.throwto */
    
    272
    -#define BlockedOnRead       5 /* Uses block_info.aiop or uses .fd or
    
    273
    -                                 .async_result */
    
    274
    -#define BlockedOnWrite      6 /* Uses block_info.aiop or uses .fd or
    
    275
    -                                 .async_result */
    
    276
    -#define BlockedOnDelay      7 /* Uses block_info.timeout or
    
    299
    +#define BlockedOnRead       5 /* Uses block_info.aiop
    
    300
    +                                 or with BlockInfoForceNonClosure
    
    301
    +                                 uses .fd or .async_reqID */
    
    302
    +#define BlockedOnWrite      6 /* Uses block_info.aiop
    
    303
    +                                 or with BlockInfoForceNonClosure
    
    304
    +                                 uses .fd or .async_reqID */
    
    305
    +#define BlockedOnDelay      7 /* Uses block_info.timeout
    
    306
    +                                 or with BlockInfoForceNonClosure
    
    277 307
                                      uses .target */
    
    278 308
     
    
    279 309
     #define BlockedOnSTM                  8 /* Uses block_info.unused */
    
    280 310
     #define BlockedOnCCall                9 /* Uses block_info.unused */
    
    281 311
     #define BlockedOnCCall_Interruptible 10 /* Uses block_info.unused
    
    282
    -                                        * Same as BlockedOnCCall but permits
    
    283
    -                                        * killing the worker thread */
    
    312
    +                                         * Same as BlockedOnCCall but permits
    
    313
    +                                         * killing the worker thread */
    
    284 314
     #define ThreadMigrating              11 /* Uses block_info.unused */
    
    285
    -#define BlockedOnDoProc              12 /* Uses block_info.async_result
    
    286
    -                                         * used by win32-legacy I/O manager */
    
    315
    +#define BlockedOnDoProc              12 /* Uses block_info.async_reqID */
    
    316
    +
    
    317
    +/* Reserved values, not values that why_blocked currently use. They
    
    318
    + * are used in primop stg_threadStatuszh and must not overlap with
    
    319
    + * other why_blocked status values. They could be changed, if the
    
    320
    + * threadStatus in ghc-internal is updated too.
    
    321
    + */
    
    322
    +#define BlockedThreadComplete 16
    
    323
    +#define BlockedThreadKilled   17
    
    287 324
     
    
    288 325
     /* Next available non-closure why_blocked tag numbers are: 13,14,15
    
    289 326
      * For more closure tag numbers, shift up all the non-closure ones
    

  • 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
    
    ... ... @@ -98,7 +107,21 @@ typedef union {
    98 107
      * have the reason in the why_blocked field of the TSO, and some
    
    99 108
      * further info (such as the closure the thread is blocked on, or the
    
    100 109
      * file descriptor if the thread is waiting on I/O) in the block_info
    
    101
    - * field.
    
    110
    + * field. See Constants.h for the why_blocked values.
    
    111
    + *
    
    112
    + * The why_blocked field must be updated atomically. The protocol for
    
    113
    + * updating block_info and why_blocked fields together is as follows:
    
    114
    + *
    
    115
    + *   Writes:
    
    116
    + *     - first write block_info (normal non-atomic write)
    
    117
    + *     - then write why_blocked with an atomic *store release*
    
    118
    + *
    
    119
    + *   Reads:
    
    120
    + *     - first read why_blocked with an atomic *load acquire*
    
    121
    + *     - then read block_info (normal non-atomic read)
    
    122
    + *
    
    123
    + *   Read of only why_blocked without block_info:
    
    124
    + *     - read why_blocked with an atomic *relaxed load*
    
    102 125
      */
    
    103 126
     
    
    104 127
     typedef struct StgTSO_ {
    
    ... ... @@ -148,11 +171,7 @@ typedef struct StgTSO_ {
    148 171
         StgWord16               what_next;      // Values defined in Constants.h
    
    149 172
         StgWord32               flags;          // Values defined in Constants.h
    
    150 173
     
    
    151
    -    /*
    
    152
    -     * N.B. why_blocked only has a handful of values but must be atomically
    
    153
    -     * updated; the smallest width which AArch64 supports for is 32-bits.
    
    154
    -     */
    
    155
    -    StgWord32               why_blocked;    // Values defined in Constants.h
    
    174
    +    StgThreadWhyBlocked     why_blocked;    // Values defined in Constants.h
    
    156 175
         StgTSOBlockInfo         block_info;     // Barrier provided by why_blocked
    
    157 176
         StgThreadID             id;
    
    158 177
         StgWord32               saved_errno;
    

  • rts/posix/Poll.c
    ... ... @@ -183,8 +183,9 @@ bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    183 183
         aiop->notify.tso     = tso;
    
    184 184
         aiop->notify_type    = NotifyTSO;
    
    185 185
         aiop->live           = &stg_ASYNCIO_LIVE0_closure;
    
    186
    -    tso->why_blocked     = rw == IORead ? BlockedOnRead : BlockedOnWrite;
    
    187 186
         tso->block_info.aiop = aiop;
    
    187
    +    RELEASE_STORE(&tso->why_blocked, rw == IORead ? BlockedOnRead
    
    188
    +                                                  : BlockedOnWrite);
    
    188 189
         return asyncIOWaitReadyPoll(iomgr, aiop, rw, fd);
    
    189 190
     }
    
    190 191
     
    
    ... ... @@ -299,10 +300,9 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    299 300
                      * cap because the tso was not on the run queue of any cap and
    
    300 301
                      * so is not subject to thread migration.
    
    301 302
                      */
    
    302
    -                StgTSO *tso      = aiop->notify.tso;
    
    303
    -                tso->why_blocked = NotBlocked;
    
    304
    -                tso->_link       = END_TSO_QUEUE;
    
    303
    +                StgTSO *tso = aiop->notify.tso;
    
    305 304
                     pushOnRunQueue(iomgr->cap, tso);
    
    305
    +                RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    306 306
                 }
    
    307 307
                 /* For the TSO case, the aiop was only reachable from the TSO
    
    308 308
                  * itself, and thus it is now no longer be reachable at all.
    

  • rts/posix/Select.c
    ... ... @@ -138,11 +138,10 @@ static bool wakeUpSleepingThreads (CapIOManager *iomgr, LowResTime now)
    138 138
                 break;
    
    139 139
             }
    
    140 140
             iomgr->sleeping_queue = tso->_link;
    
    141
    -        RELAXED_STORE(&tso->why_blocked, NotBlocked);
    
    142
    -        tso->_link = END_TSO_QUEUE;
    
    143 141
             IF_DEBUG(scheduler, debugBelch("Waking up sleeping thread %"
    
    144 142
                                            FMT_StgThreadID "\n", tso->id));
    
    145 143
             pushOnRunQueue(iomgr->cap,tso);
    
    144
    +        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    146 145
             flag = true;
    
    147 146
         }
    
    148 147
         return flag;
    
    ... ... @@ -311,7 +310,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    311 310
            * So the (int) cast should be removed across the code base once
    
    312 311
            * GHC requires a version of FreeBSD that has that change in it.
    
    313 312
            */
    
    314
    -        switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    313
    +        switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    315 314
             case BlockedOnRead:
    
    316 315
               {
    
    317 316
                 int fd = tso->block_info.fd;
    
    ... ... @@ -449,7 +448,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    449 448
                   int fd;
    
    450 449
                   enum FdState fd_state = RTS_FD_IS_BLOCKING;
    
    451 450
     
    
    452
    -              switch (tso->why_blocked) {
    
    451
    +              switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    453 452
                   case BlockedOnRead:
    
    454 453
                       fd = tso->block_info.fd;
    
    455 454
     
    
    ... ... @@ -488,9 +487,8 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    488 487
                       IF_DEBUG(scheduler,
    
    489 488
                           debugBelch("Waking up blocked thread %" FMT_StgThreadID "\n",
    
    490 489
                                      tso->id));
    
    491
    -                  tso->why_blocked = NotBlocked;
    
    492
    -                  tso->_link = END_TSO_QUEUE;
    
    493 490
                       pushOnRunQueue(iomgr->cap,tso);
    
    491
    +                  RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    494 492
                       break;
    
    495 493
                   case RTS_FD_IS_BLOCKING:
    
    496 494
                       if (prev == NULL)
    

  • rts/posix/Timeout.c
    ... ... @@ -48,8 +48,8 @@ bool syncDelayTimeout(CapIOManager *iomgr, StgTSO *tso, HsInt us_delay)
    48 48
         initElemTimeoutQueue(timeout, notify, NotifyTSO, iomgr->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(&iomgr->timeout_queue, timeout, target);
    
    55 55
     
    
    ... ... @@ -116,10 +116,9 @@ static void notifyTimeoutCompletion(CapIOManager *iomgr, StgTimeout *timeout)
    116 116
         switch (timeout->notify_type) {
    
    117 117
             case NotifyTSO:
    
    118 118
             {
    
    119
    -            StgTSO *tso      = timeout->notify.tso;
    
    120
    -            tso->why_blocked = NotBlocked;
    
    121
    -            tso->_link       = END_TSO_QUEUE;
    
    119
    +            StgTSO *tso = timeout->notify.tso;
    
    122 120
                 pushOnRunQueue(iomgr->cap, tso);
    
    121
    +            RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    123 122
                 break;
    
    124 123
             }
    
    125 124
             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
    +        ASSERT(!IsBlockInfoClosure(why_blocked));
    
    803
    +        ASSERT(tso->block_info.unused == END_TSO_QUEUE);
    
    804
    +        break;
    
    805
    +
    
    806
    +#if !defined(THREADED_RTS)
    
    807
    +    // Only these three can use BlockInfoForceNonClosure
    
    808
    +    case BlockedOnRead  | BlockInfoForceNonClosure:
    
    809
    +    case BlockedOnWrite | BlockInfoForceNonClosure:
    
    810
    +    case BlockedOnDelay | BlockInfoForceNonClosure:
    
    811
    +#if defined(mingw32_HOST_OS)
    
    812
    +    case BlockedOnDoProc:
    
    813
    +#endif
    
    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,21 +138,9 @@ 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
    
    ... ... @@ -160,7 +148,6 @@ scavengeTSO (StgTSO *tso)
    160 148
         // BlockedOnRead/BlockedOnWrite) or a time value (BlockedOnDelay)
    
    161 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
    ... ... @@ -304,7 +304,7 @@ start:
    304 304
                 for(tso = iomgr->blocked_queue_hd; tso != END_TSO_QUEUE;
    
    305 305
                       tso = tso->_link) {
    
    306 306
     
    
    307
    -                switch(ACQUIRE_LOAD(&tso->why_blocked)) {
    
    307
    +                switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
    
    308 308
                     case BlockedOnRead:
    
    309 309
                     case BlockedOnWrite:
    
    310 310
                     case BlockedOnDoProc:
    
    ... ... @@ -324,8 +324,6 @@ start:
    324 324
                             }
    
    325 325
     
    
    326 326
                             // Terminates the run queue + this inner for-loop.
    
    327
    -                        tso->_link = END_TSO_QUEUE;
    
    328
    -                        tso->why_blocked = NotBlocked;
    
    329 327
                             // For stg_block_async frames (read/write/doProc),
    
    330 328
                             // write len and errCode directly to the stack.
    
    331 329
                             // For stg_block_noregs frames (delay), nothing
    
    ... ... @@ -335,14 +333,14 @@ start:
    335 333
                                 tso->stackobj->sp[2] = (W_)errCode;
    
    336 334
                             }
    
    337 335
                             pushOnRunQueue(&MainCapability, tso);
    
    336
    +                        RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    338 337
                             break;
    
    339 338
                         }
    
    340 339
                         break;
    
    341
    -                default:
    
    342
    -                    if (tso->why_blocked != NotBlocked) {
    
    343
    -                        barf("awaitRequests: odd thread state");
    
    344
    -                    }
    
    340
    +                case NotBlocked:
    
    345 341
                         break;
    
    342
    +                default:
    
    343
    +                    barf("awaitRequests: odd thread state");
    
    346 344
                     }
    
    347 345
     
    
    348 346
                     prev = tso;