Rodrigo Mesquita pushed to branch wip/romes/27729 at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • rts/CloneStack.c
    ... ... @@ -88,6 +88,7 @@ void sendCloneStackMessage(StgTSO *tso, HsStablePtr mvar) {
    88 88
     void handleCloneStackMessage(Capability *cap, MessageCloneStack *msg){
    
    89 89
       // We must check that the current owner of the thread we want to clone the stack for
    
    90 90
       // is still this capability.
    
    91
    +  // See Note [TSO owner may change in between Msg being sent and received]
    
    91 92
       Capability *owner = RELAXED_LOAD(&msg->tso->cap);
    
    92 93
       if (owner != cap) {
    
    93 94
         // The target TSO may have migrated after the message was queued on the old
    

  • rts/Messages.c
    ... ... @@ -66,6 +66,62 @@ void sendMessage(Capability *from_cap, Capability *to_cap, Message *msg)
    66 66
        Handle a message
    
    67 67
        ------------------------------------------------------------------------- */
    
    68 68
     
    
    69
    +/*
    
    70
    +Note [TSO owner may change in between Msg being sent and received]
    
    71
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    72
    +When a message is sent from Capability (C1) to a target TSO (T2) (e.g.
    
    73
    +MessageUpdTSOFlag, MessageCloneStack, ...), it is queued on the TSO's owner
    
    74
    +Capability (C3) inbox (inboxes are owned by Capabilities, not TSOs).
    
    75
    +
    
    76
    +At a later point, the Capability (C3) will process its inbox. Upon receiving
    
    77
    +the message meant for a specific TSO (T2), it must first always check that the
    
    78
    +TSO's owner is *still* itself (C3).
    
    79
    +
    
    80
    +The target TSO (T2) may have migrated after the message was queued on its old
    
    81
    +capability (C3). In that case we must forward the request to the new owner
    
    82
    +(say, C4); otherwise the Capability C3 could be modifying a TSO it no longer
    
    83
    +owns, racing with its actual owner mutating it, since it is no longer the owner.
    
    84
    +
    
    85
    +The message meant for a TSO should only be executed when the receiving
    
    86
    +Capability is still the owner of that TSO. Otherwise, it must be forwarded to
    
    87
    +the new owner.
    
    88
    +
    
    89
    +The general pattern is one where there's a top-level function which assumes it
    
    90
    +can be called by capabilities other than the TSO's owner. The function checks
    
    91
    +whether the current capability is the TSO owner. If yes, execute the action. If
    
    92
    +not, then it sends a message to the current TSO's owner. On receiving the
    
    93
    +message, the new capability will just call that top-level function, which will
    
    94
    +ensure the message is forwarded again if the TSO owner changed.
    
    95
    +It will look something like:
    
    96
    +
    
    97
    +  runMyMsg(Capability *from, StgTSO *target, ...) {
    
    98
    +
    
    99
    +#if defined(THREADED_RTS)
    
    100
    +    Capability *owner = RELAXED_LOAD(&target->cap)
    
    101
    +    if (owner != from) {
    
    102
    +      MessageMyMsg* msg = ...
    
    103
    +      sendMessage(cap, owner, msg)
    
    104
    +      return
    
    105
    +    }
    
    106
    +#endif
    
    107
    +
    
    108
    +    actuallyDoTheWork(...)
    
    109
    +  }
    
    110
    +
    
    111
    +  executeMessage(...) {
    
    112
    +
    
    113
    +    if (i == &stg_MY_MSG_info) {
    
    114
    +
    
    115
    +      MessageMyMsg* msg = (MessageMyMsg*) m
    
    116
    +      runMyMsg(cap, m->tso, ...)
    
    117
    +
    
    118
    +    }
    
    119
    +  }
    
    120
    +
    
    121
    +See example `updThreadFlag` and `executeMessage`'s `stg_MSG_UPD_TSO_FLAG_info`,
    
    122
    +or `tryWakeUpThread` and `stg_MSG_TRY_WAKEUP_info` for two live examples.
    
    123
    +*/
    
    124
    +
    
    69 125
     #if defined(THREADED_RTS)
    
    70 126
     
    
    71 127
     void
    
    ... ... @@ -142,13 +198,9 @@ loop:
    142 198
         }
    
    143 199
         else if(i == &stg_MSG_UPD_TSO_FLAG_info){
    
    144 200
             MessageUpdTSOFlag *u = (MessageUpdTSOFlag*) m;
    
    145
    -        if (u->set) {
    
    146
    -            u->tso->flags |= u->flag;
    
    147
    -        }
    
    148
    -        else {
    
    149
    -            u->tso->flags &= ~u->flag;
    
    150
    -        }
    
    151
    -        return;
    
    201
    +
    
    202
    +        StgTSO *tso = RELAXED_LOAD(&u->tso);
    
    203
    +        updThreadFlag(cap, tso, u->flag, u->set);
    
    152 204
         }
    
    153 205
         else
    
    154 206
         {
    

  • rts/Threads.c
    ... ... @@ -379,23 +379,25 @@ migrateThread (Capability *from, StgTSO *tso, Capability *to)
    379 379
        sets or unsets a flag in a given TSO
    
    380 380
        ------------------------------------------------------------------------- */
    
    381 381
     
    
    382
    -static void
    
    383
    -updThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag, StgBool set);
    
    384
    -
    
    385 382
     void setThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag)
    
    386 383
     {
    
    387
    -    updThreadFlag(from, tso, flag, 1);
    
    384
    +    updThreadFlag(from, tso, flag, true);
    
    388 385
     }
    
    389 386
     
    
    390 387
     void unsetThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag)
    
    391 388
     {
    
    392
    -    updThreadFlag(from, tso, flag, 0);
    
    389
    +    updThreadFlag(from, tso, flag, false);
    
    393 390
     }
    
    394 391
     
    
    395
    -static void
    
    396
    -updThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag, StgBool set /* true=set, false=unset */)
    
    392
    +void
    
    393
    +updThreadFlag(Capability *from USED_IF_THREADS, StgTSO *tso, StgWord32 flag, StgBool set /* true=set, false=unset */)
    
    397 394
     {
    
    398 395
     #if defined(THREADED_RTS)
    
    396
    +    // If we're the current owner of the thread we want to modify, do it.
    
    397
    +    // Otherwise, we must forward the message to the actual owner.
    
    398
    +    // When executing the upd message, we check again that we're still the TSO
    
    399
    +    // owner (which may have changed since the message was queued on this cap.)
    
    400
    +    // See Note [TSO owner may change in between Msg being sent and received]
    
    399 401
         Capability *tso_owner = RELAXED_LOAD(&tso->cap);
    
    400 402
         if (from != tso_owner) {
    
    401 403
           MessageUpdTSOFlag *msg;
    
    ... ... @@ -407,8 +409,6 @@ updThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag, StgBool set /* true
    407 409
           sendMessage(from, tso_owner, (Message*)msg);
    
    408 410
           return;
    
    409 411
         }
    
    410
    -#else
    
    411
    -    (void)from; // unused in non-threaded case
    
    412 412
     #endif
    
    413 413
     
    
    414 414
         if (set) {
    

  • rts/Threads.h
    ... ... @@ -21,6 +21,7 @@ void migrateThread (Capability *from, StgTSO *tso, Capability *to);
    21 21
     
    
    22 22
     void setThreadFlag       (Capability *from, StgTSO *tso, StgWord32 flag);
    
    23 23
     void unsetThreadFlag     (Capability *from, StgTSO *tso, StgWord32 flag);
    
    24
    +void updThreadFlag       (Capability *from, StgTSO *tso, StgWord32 flag, StgBool set);
    
    24 25
     
    
    25 26
     // Wakes up a thread on a Capability (probably a different Capability
    
    26 27
     // from the one held by the current Task).