Rodrigo Mesquita pushed to branch wip/romes/27729 at Glasgow Haskell Compiler / GHC
Commits:
-
16112a3c
by Rodrigo Mesquita at 2026-08-24T16:02:49+01:00
3 changed files:
Changes:
| ... | ... | @@ -84,27 +84,41 @@ owns, racing with its actual owner mutating it, since it is no longer the owner. |
| 84 | 84 | |
| 85 | 85 | The message meant for a TSO should only be executed when the receiving
|
| 86 | 86 | Capability is still the owner of that TSO. Otherwise, it must be forwarded to
|
| 87 | -the new owner. The pseudo code for handling a message that targets a particular
|
|
| 88 | -TSO will look something like:
|
|
| 87 | +the new owner.
|
|
| 88 | + |
|
| 89 | +The general pattern is one where there's a top-level function which assumes can
|
|
| 90 | +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 | + }
|
|
| 89 | 110 | |
| 90 | 111 | executeMessage(...) {
|
| 91 | 112 | |
| 92 | 113 | if (i == &stg_MY_MSG_info) {
|
| 93 | 114 | |
| 94 | 115 | MessageMyMsg* msg = (MessageMyMsg*) m
|
| 95 | - |
|
| 96 | - Capability *owner = RELAXED_LOAD(&msg->tso->cap);
|
|
| 97 | - if (owner != cap) {
|
|
| 98 | - sendMessage(cap, owner, (Message *)msg);
|
|
| 99 | - return;
|
|
| 100 | - }
|
|
| 101 | - |
|
| 102 | - actuallyExecute(...)
|
|
| 116 | + runMyMsg(cap, m->tso, ...)
|
|
| 103 | 117 | |
| 104 | 118 | }
|
| 105 | 119 | }
|
| 106 | 120 | |
| 107 | -See `executeMessage`'s `stg_MSG_UPD_TSO_FLAG_info` and
|
|
| 121 | +See example `updThreadFlag` and `executeMessage`'s `stg_MSG_UPD_TSO_FLAG_info`, or
|
|
| 108 | 122 | `stg_MSG_CLONE_STACK_info` for two live examples.
|
| 109 | 123 | */
|
| 110 | 124 | |
| ... | ... | @@ -185,18 +199,8 @@ loop: |
| 185 | 199 | else if(i == &stg_MSG_UPD_TSO_FLAG_info){
|
| 186 | 200 | MessageUpdTSOFlag *u = (MessageUpdTSOFlag*) m;
|
| 187 | 201 | |
| 188 | - // We must check that the current owner of the thread is still this capability.
|
|
| 189 | - // See Note [TSO owner may change in between Msg being sent and received]
|
|
| 190 | - Capability *owner = RELAXED_LOAD(&u->tso->cap);
|
|
| 191 | - if (owner != cap) {
|
|
| 192 | - sendMessage(cap, owner, (Message *)u);
|
|
| 193 | - return;
|
|
| 194 | - }
|
|
| 195 | - |
|
| 196 | - if (u->set) { u->tso->flags |= u->flag; }
|
|
| 197 | - else { u->tso->flags &= ~u->flag; }
|
|
| 198 | - |
|
| 199 | - return;
|
|
| 202 | + StgTSO *tso = RELAXED_LOAD(&u->tso);
|
|
| 203 | + updThreadFlag(cap, tso, u->flag, u->set);
|
|
| 200 | 204 | }
|
| 201 | 205 | else
|
| 202 | 206 | {
|
| ... | ... | @@ -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) {
|
| ... | ... | @@ -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).
|