Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

6 changed files:

Changes:

  • changelog.d/fix-blackhole-handling
    1
    +section: rts
    
    2
    +synopsis: Fix several black hole handling bugs that could lead to deadlocks
    
    3
    +  or crashes in multithreaded programs. These could show up as the program
    
    4
    +  hanging or "END_TSO_QUEUE object entered" errors.
    
    5
    +issues: #26922 #26936
    
    6
    +mrs: !15640

  • rts/Messages.c
    ... ... @@ -188,10 +188,7 @@ uint32_t messageBlackHole(Capability *cap, MessageBlackHole *msg)
    188 188
         // BLACKHOLE has already been updated, and GC has shorted out the
    
    189 189
         // indirection, so the pointer no longer points to a BLACKHOLE at
    
    190 190
         // all.
    
    191
    -    if (bh_info != &stg_BLACKHOLE_info &&
    
    192
    -        bh_info != &stg_CAF_BLACKHOLE_info &&
    
    193
    -        bh_info != &__stg_EAGER_BLACKHOLE_info &&
    
    194
    -        bh_info != &stg_WHITEHOLE_info) {
    
    191
    +    if (!IS_BLACKHOLE_OR_WHITEHOLE_INFO(bh_info)) {
    
    195 192
             return 0;
    
    196 193
         }
    
    197 194
     
    
    ... ... @@ -350,10 +347,7 @@ StgTSO * blackHoleOwner (StgClosure *bh)
    350 347
     
    
    351 348
         info = RELAXED_LOAD(&bh->header.info);
    
    352 349
     
    
    353
    -    if (info != &stg_BLACKHOLE_info &&
    
    354
    -        info != &stg_CAF_BLACKHOLE_info &&
    
    355
    -        info != &__stg_EAGER_BLACKHOLE_info &&
    
    356
    -        info != &stg_WHITEHOLE_info) {
    
    350
    +    if (!IS_BLACKHOLE_OR_WHITEHOLE_INFO(info)) {
    
    357 351
             return NULL;
    
    358 352
         }
    
    359 353
     
    

  • rts/ThreadPaused.c
    ... ... @@ -183,6 +183,30 @@ stackSqueeze(Capability *cap, StgTSO *tso, StgPtr bottom)
    183 183
         }
    
    184 184
     }
    
    185 185
     
    
    186
    +/*
    
    187
    + * Check whether tso is the owner of the black hole bh.
    
    188
    + *
    
    189
    + * We must call this from the capability that runs tso,
    
    190
    + * since that guarantees that the writes to bh->indirectee
    
    191
    + * by tso claiming ownership have been visible. If another
    
    192
    + * tso has claimed it again afterwards we can safely suspend
    
    193
    + * our work.
    
    194
    + */
    
    195
    +static bool
    
    196
    +threadPausedBlackHoleOwner(StgTSO *tso, StgClosure *bh)
    
    197
    +{
    
    198
    +    StgClosure *ind = RELAXED_LOAD(&((StgInd*)bh)->indirectee);
    
    199
    +    if (ind == (StgClosure*)tso) {
    
    200
    +        return true;
    
    201
    +    }
    
    202
    +    const StgInfoTable *ind_info = GET_INFO(UNTAG_CLOSURE(ind));
    
    203
    +    if (ind_info == &stg_BLOCKING_QUEUE_CLEAN_info
    
    204
    +        || ind_info == &stg_BLOCKING_QUEUE_DIRTY_info) {
    
    205
    +        return ((StgBlockingQueue*)UNTAG_CLOSURE(ind))->owner == tso;
    
    206
    +    }
    
    207
    +    return false;
    
    208
    +}
    
    209
    +
    
    186 210
     /* -----------------------------------------------------------------------------
    
    187 211
      * Pausing a thread
    
    188 212
      *
    
    ... ... @@ -255,11 +279,10 @@ threadPaused(Capability *cap, StgTSO *tso)
    255 279
                 // Note [suspend duplicate work]
    
    256 280
                 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    257 281
                 // If the info table is a WHITEHOLE or a BLACKHOLE, then
    
    258
    -            // another thread has claimed it (via the SET_INFO()
    
    259
    -            // below), or is in the process of doing so.  In that case
    
    260
    -            // we want to suspend the work that the current thread has
    
    261
    -            // done on this thunk and wait until the other thread has
    
    262
    -            // finished.
    
    282
    +            // some thread has claimed it, or is in the process of doing
    
    283
    +            // so. In that case we want to suspend the work that the
    
    284
    +            // current thread has done on this thunk and wait until the
    
    285
    +            // other thread has finished.
    
    263 286
                 //
    
    264 287
                 // If eager blackholing is taking place, it could be the
    
    265 288
                 // case that the blackhole points to the current
    
    ... ... @@ -287,8 +310,8 @@ threadPaused(Capability *cap, StgTSO *tso)
    287 310
                 // Note that great care is required when entering computations
    
    288 311
                 // suspended by this mechanism. See Note [AP_STACKs must be eagerly
    
    289 312
                 // blackholed] for details.
    
    290
    -            if (((bh_info == &stg_BLACKHOLE_info)
    
    291
    -                 && (RELAXED_LOAD(&((StgInd*)bh)->indirectee) != (StgClosure*)tso))
    
    313
    +            if ((IS_BLACKHOLE_INFO(bh_info)
    
    314
    +                && !threadPausedBlackHoleOwner(tso, bh))
    
    292 315
                     || (bh_info == &stg_WHITEHOLE_info))
    
    293 316
                 {
    
    294 317
                     debugTrace(DEBUG_squeeze,
    
    ... ... @@ -318,14 +341,13 @@ threadPaused(Capability *cap, StgTSO *tso)
    318 341
                 // If we have a frame that is already eagerly blackholed, we
    
    319 342
                 // shouldn't overwrite its payload: There may already be a blocking
    
    320 343
                 // queue (see #26324).
    
    321
    -            if(frame_info == &stg_bh_upd_frame_info) {
    
    322
    -                // eager black hole: we do nothing
    
    344
    +            if(frame_info == &stg_bh_upd_frame_info
    
    345
    +               || IS_BLACKHOLE_INFO(bh_info)) {
    
    346
    +                // already a black hole: we do nothing
    
    323 347
     
    
    324 348
                     // it should be a black hole (but we may not own it, as another
    
    325 349
                     // thread could have raced us to claim it)
    
    326
    -                ASSERT(bh_info == &stg_BLACKHOLE_info ||
    
    327
    -                       bh_info == &__stg_EAGER_BLACKHOLE_info ||
    
    328
    -                       bh_info == &stg_CAF_BLACKHOLE_info);
    
    350
    +                ASSERT(IS_BLACKHOLE_INFO(bh_info));
    
    329 351
     
    
    330 352
                 } else {
    
    331 353
                     // lazy black hole
    

  • rts/Threads.c
    ... ... @@ -474,7 +474,7 @@ checkBlockingQueues (Capability *cap, StgTSO *tso)
    474 474
             // thing the result would be the same in almost all cases. See #20093.
    
    475 475
             p = UNTAG_CLOSURE(bq->bh);
    
    476 476
             const StgInfoTable *pinfo = ACQUIRE_LOAD(&p->header.info);
    
    477
    -        if (pinfo != &stg_BLACKHOLE_info ||
    
    477
    +        if (!IS_BLACKHOLE_INFO(pinfo) ||
    
    478 478
                 (RELAXED_LOAD(&((StgInd *)p)->indirectee) != (StgClosure*)bq))
    
    479 479
             {
    
    480 480
                 wakeBlockingQueue(cap,bq);
    
    ... ... @@ -498,10 +498,7 @@ updateThunk (Capability *cap, StgTSO *tso, StgClosure *thunk, StgClosure *val)
    498 498
         const StgInfoTable *i;
    
    499 499
     
    
    500 500
         i = ACQUIRE_LOAD(&thunk->header.info);
    
    501
    -    if (i != &stg_BLACKHOLE_info &&
    
    502
    -        i != &stg_CAF_BLACKHOLE_info &&
    
    503
    -        i != &__stg_EAGER_BLACKHOLE_info &&
    
    504
    -        i != &stg_WHITEHOLE_info) {
    
    501
    +    if (!IS_BLACKHOLE_OR_WHITEHOLE_INFO(i)) {
    
    505 502
             updateWithIndirection(cap, thunk, val);
    
    506 503
             return;
    
    507 504
         }
    

  • rts/Updates.h
    ... ... @@ -190,9 +190,9 @@
    190 190
      * frame is encountered, it checks the info table of the updatee and:
    
    191 191
      *
    
    192 192
      *  - if it is `BLACKHOLE`, then the thunk has already been claimed for evaluation
    
    193
    - *    by another thread, and the yielding thread is instead added to the
    
    194
    - *    `BLACKHOLE`'s blocking queue (see Note [suspend duplicate work] in
    
    195
    - *    `ThreadPaused.c`).
    
    193
    + *    by some thread. If that's not the yielding thread itself, the yielding thread
    
    194
    + *    is added to the `BLACKHOLE`'s blocking queue (see Note [suspend duplicate
    
    195
    + *    work] in `ThreadPaused.c`).
    
    196 196
      *
    
    197 197
      *  - if not, then it blackholes the thunk as done in eager blackholing (but
    
    198 198
      *    using the `BLACKHOLE_info` info table instead of `EAGER_BLACKHOLE_info`).
    

  • rts/include/rts/storage/ClosureMacros.h
    ... ... @@ -382,6 +382,18 @@ EXTERN_INLINE StgOffset BLACKHOLE_sizeW ( void );
    382 382
     EXTERN_INLINE StgOffset BLACKHOLE_sizeW ( void )
    
    383 383
     { return sizeofW(StgInd); } // a BLACKHOLE is a kind of indirection
    
    384 384
     
    
    385
    +/* -----------------------------------------------------------------------------
    
    386
    +   Blackhole predicates
    
    387
    +   -------------------------------------------------------------------------- */
    
    388
    +
    
    389
    +#define IS_BLACKHOLE_INFO(info) \
    
    390
    +    ((info) == &stg_BLACKHOLE_info || \
    
    391
    +     (info) == &__stg_EAGER_BLACKHOLE_info || \
    
    392
    +     (info) == &stg_CAF_BLACKHOLE_info)
    
    393
    +
    
    394
    +#define IS_BLACKHOLE_OR_WHITEHOLE_INFO(info) \
    
    395
    +    (IS_BLACKHOLE_INFO(info) || (info) == &stg_WHITEHOLE_info)
    
    396
    +
    
    385 397
     /* --------------------------------------------------------------------------
    
    386 398
        Sizes of closures
    
    387 399
        ------------------------------------------------------------------------*/