Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
bb324171
by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
-
ed99b7b7
by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
8 changed files:
- rts/CloneStack.c
- rts/Interpreter.c
- rts/Messages.c
- rts/StgMiscClosures.cmm
- rts/Threads.c
- rts/Threads.h
- rts/include/rts/storage/Closures.h
- rts/include/stg/MiscClosures.h
Changes:
| ... | ... | @@ -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
|
| ... | ... | @@ -416,22 +416,14 @@ void rts_disableStopNextBreakpointAll(void) |
| 416 | 416 | |
| 417 | 417 | void rts_enableStopNextBreakpoint(StgTSO* tso)
|
| 418 | 418 | {
|
| 419 | -#if defined(THREADED_RTS)
|
|
| 420 | 419 | Capability* cap = rts_unsafeGetMyCapability();
|
| 421 | 420 | setThreadFlag(cap, tso, TSO_STOP_NEXT_BREAKPOINT);
|
| 422 | -#else
|
|
| 423 | - tso->flags |= TSO_STOP_NEXT_BREAKPOINT;
|
|
| 424 | -#endif
|
|
| 425 | 421 | }
|
| 426 | 422 | |
| 427 | 423 | void rts_disableStopNextBreakpoint(StgTSO* tso)
|
| 428 | 424 | {
|
| 429 | -#if defined(THREADED_RTS)
|
|
| 430 | 425 | Capability* cap = rts_unsafeGetMyCapability();
|
| 431 | 426 | unsetThreadFlag(cap, tso, TSO_STOP_NEXT_BREAKPOINT);
|
| 432 | -#else
|
|
| 433 | - tso->flags &= ~TSO_STOP_NEXT_BREAKPOINT;
|
|
| 434 | -#endif
|
|
| 435 | 427 | }
|
| 436 | 428 | |
| 437 | 429 | /* ---------------------------------------------------------------------------
|
| ... | ... | @@ -440,22 +432,14 @@ void rts_disableStopNextBreakpoint(StgTSO* tso) |
| 440 | 432 | |
| 441 | 433 | void rts_enableStopAfterReturn(StgTSO* tso)
|
| 442 | 434 | {
|
| 443 | -#if defined(THREADED_RTS)
|
|
| 444 | 435 | Capability* cap = rts_unsafeGetMyCapability();
|
| 445 | 436 | setThreadFlag(cap, tso, TSO_STOP_AFTER_RETURN);
|
| 446 | -#else
|
|
| 447 | - tso->flags |= TSO_STOP_AFTER_RETURN;
|
|
| 448 | -#endif
|
|
| 449 | 437 | }
|
| 450 | 438 | |
| 451 | 439 | void rts_disableStopAfterReturn(StgTSO* tso)
|
| 452 | 440 | {
|
| 453 | -#if defined(THREADED_RTS)
|
|
| 454 | 441 | Capability* cap = rts_unsafeGetMyCapability();
|
| 455 | 442 | unsetThreadFlag(cap, tso, TSO_STOP_AFTER_RETURN);
|
| 456 | -#else
|
|
| 457 | - tso->flags &= ~TSO_STOP_AFTER_RETURN;
|
|
| 458 | -#endif
|
|
| 459 | 443 | }
|
| 460 | 444 | |
| 461 | 445 | /*
|
| ... | ... | @@ -36,8 +36,7 @@ void sendMessage(Capability *from_cap, Capability *to_cap, Message *msg) |
| 36 | 36 | i != &stg_IND_info && // can happen if a MSG_BLACKHOLE is revoked
|
| 37 | 37 | i != &stg_WHITEHOLE_info &&
|
| 38 | 38 | i != &stg_MSG_CLONE_STACK_info &&
|
| 39 | - i != &stg_MSG_SET_TSO_FLAG_info &&
|
|
| 40 | - i != &stg_MSG_UNSET_TSO_FLAG_info) {
|
|
| 39 | + i != &stg_MSG_UPD_TSO_FLAG_info) {
|
|
| 41 | 40 | barf("sendMessage: %p", i);
|
| 42 | 41 | }
|
| 43 | 42 | }
|
| ... | ... | @@ -67,6 +66,62 @@ void sendMessage(Capability *from_cap, Capability *to_cap, Message *msg) |
| 67 | 66 | Handle a message
|
| 68 | 67 | ------------------------------------------------------------------------- */
|
| 69 | 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 | + |
|
| 70 | 125 | #if defined(THREADED_RTS)
|
| 71 | 126 | |
| 72 | 127 | void
|
| ... | ... | @@ -141,15 +196,11 @@ loop: |
| 141 | 196 | MessageCloneStack *cloneStackMessage = (MessageCloneStack*) m;
|
| 142 | 197 | handleCloneStackMessage(cap, cloneStackMessage);
|
| 143 | 198 | }
|
| 144 | - else if(i == &stg_MSG_SET_TSO_FLAG_info){
|
|
| 199 | + else if(i == &stg_MSG_UPD_TSO_FLAG_info){
|
|
| 145 | 200 | MessageUpdTSOFlag *u = (MessageUpdTSOFlag*) m;
|
| 146 | - u->tso->flags |= u->flag;
|
|
| 147 | - return;
|
|
| 148 | - }
|
|
| 149 | - else if(i == &stg_MSG_UNSET_TSO_FLAG_info){
|
|
| 150 | - MessageUpdTSOFlag *u = (MessageUpdTSOFlag*) m;
|
|
| 151 | - u->tso->flags &= ~u->flag;
|
|
| 152 | - return;
|
|
| 201 | + |
|
| 202 | + StgTSO *tso = RELAXED_LOAD(&u->tso);
|
|
| 203 | + updThreadFlag(cap, tso, u->flag, u->set);
|
|
| 153 | 204 | }
|
| 154 | 205 | else
|
| 155 | 206 | {
|
| ... | ... | @@ -855,11 +855,8 @@ INFO_TABLE_CONSTR(stg_MSG_NULL,1,0,0,PRIM,"MSG_NULL","MSG_NULL") |
| 855 | 855 | INFO_TABLE_CONSTR(stg_MSG_CLONE_STACK,3,0,0,PRIM,"MSG_CLONE_STACK","MSG_CLONE_STACK")
|
| 856 | 856 | { ccall pbarf("stg_MSG_CLONE_STACK object (%p) entered!", R1 "ptr") never returns; }
|
| 857 | 857 | |
| 858 | -INFO_TABLE_CONSTR(stg_MSG_SET_TSO_FLAG,2,1,0,PRIM,"MSG_SET_TSO_FLAG","MSG_SET_TSO_FLAG")
|
|
| 859 | -{ foreign "C" barf("stg_MSG_SET_TSO_FLAG object (%p) entered!", R1) never returns; }
|
|
| 860 | - |
|
| 861 | -INFO_TABLE_CONSTR(stg_MSG_UNSET_TSO_FLAG,2,1,0,PRIM,"MSG_UNSET_TSO_FLAG","MSG_UNSET_TSO_FLAG")
|
|
| 862 | -{ foreign "C" barf("stg_MSG_UNSET_TSO_FLAG object (%p) entered!", R1) never returns; }
|
|
| 858 | +INFO_TABLE_CONSTR(stg_MSG_UPD_TSO_FLAG,2,2,0,PRIM,"MSG_UPD_TSO_FLAG","MSG_UPD_TSO_FLAG")
|
|
| 859 | +{ foreign "C" barf("stg_MSG_UPD_TSO_FLAG object (%p) entered!", R1) never returns; }
|
|
| 863 | 860 | |
| 864 | 861 | /* ----------------------------------------------------------------------------
|
| 865 | 862 | END_TSO_QUEUE
|
| ... | ... | @@ -379,32 +379,46 @@ migrateThread (Capability *from, StgTSO *tso, Capability *to) |
| 379 | 379 | sets or unsets a flag in a given TSO
|
| 380 | 380 | ------------------------------------------------------------------------- */
|
| 381 | 381 | |
| 382 | -#if defined(THREADED_RTS)
|
|
| 383 | -static void
|
|
| 384 | -updThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag, const StgInfoTable* info);
|
|
| 385 | - |
|
| 386 | 382 | void setThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag)
|
| 387 | 383 | {
|
| 388 | - updThreadFlag(from, tso, flag, &stg_MSG_SET_TSO_FLAG_info);
|
|
| 384 | + updThreadFlag(from, tso, flag, true);
|
|
| 389 | 385 | }
|
| 390 | 386 | |
| 391 | 387 | void unsetThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag)
|
| 392 | 388 | {
|
| 393 | - updThreadFlag(from, tso, flag, &stg_MSG_UNSET_TSO_FLAG_info);
|
|
| 389 | + updThreadFlag(from, tso, flag, false);
|
|
| 394 | 390 | }
|
| 395 | 391 | |
| 396 | -static void
|
|
| 397 | -updThreadFlag(Capability *from, StgTSO *tso, StgWord32 flag, const StgInfoTable* info)
|
|
| 392 | +void
|
|
| 393 | +updThreadFlag(Capability *from USED_IF_THREADS, StgTSO *tso, StgWord32 flag, StgBool set /* true=set, false=unset */)
|
|
| 398 | 394 | {
|
| 399 | - MessageUpdTSOFlag *msg;
|
|
| 400 | - msg = (MessageUpdTSOFlag *)allocate(from,sizeofW(MessageUpdTSOFlag));
|
|
| 401 | - msg->tso = tso;
|
|
| 402 | - msg->flag = flag;
|
|
| 403 | - SET_HDR_RELEASE(msg, info, CCS_SYSTEM);
|
|
| 404 | - sendMessage(from, tso->cap, (Message*)msg);
|
|
| 405 | -}
|
|
| 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]
|
|
| 401 | + Capability *tso_owner = RELAXED_LOAD(&tso->cap);
|
|
| 402 | + if (from != tso_owner) {
|
|
| 403 | + MessageUpdTSOFlag *msg;
|
|
| 404 | + msg = (MessageUpdTSOFlag *)allocate(from,sizeofW(MessageUpdTSOFlag));
|
|
| 405 | + msg->tso = tso;
|
|
| 406 | + msg->flag = flag;
|
|
| 407 | + msg->set = set;
|
|
| 408 | + SET_HDR_RELEASE(msg, &stg_MSG_UPD_TSO_FLAG_info, CCS_SYSTEM);
|
|
| 409 | + sendMessage(from, tso_owner, (Message*)msg);
|
|
| 410 | + return;
|
|
| 411 | + }
|
|
| 406 | 412 | #endif
|
| 407 | 413 | |
| 414 | + if (set) {
|
|
| 415 | + tso->flags |= flag;
|
|
| 416 | + }
|
|
| 417 | + else {
|
|
| 418 | + tso->flags &= ~flag;
|
|
| 419 | + }
|
|
| 420 | +}
|
|
| 421 | + |
|
| 408 | 422 | /* ----------------------------------------------------------------------------
|
| 409 | 423 | awakenBlockedQueue
|
| 410 | 424 |
| ... | ... | @@ -19,10 +19,9 @@ void checkBlockingQueues (Capability *cap, StgTSO *tso); |
| 19 | 19 | void tryWakeupThread (Capability *cap, StgTSO *tso);
|
| 20 | 20 | void migrateThread (Capability *from, StgTSO *tso, Capability *to);
|
| 21 | 21 | |
| 22 | -#if defined(THREADED_RTS)
|
|
| 23 | 22 | void setThreadFlag (Capability *from, StgTSO *tso, StgWord32 flag);
|
| 24 | 23 | void unsetThreadFlag (Capability *from, StgTSO *tso, StgWord32 flag);
|
| 25 | -#endif
|
|
| 24 | +void updThreadFlag (Capability *from, StgTSO *tso, StgWord32 flag, StgBool set);
|
|
| 26 | 25 | |
| 27 | 26 | // Wakes up a thread on a Capability (probably a different Capability
|
| 28 | 27 | // from the one held by the current Task).
|
| ... | ... | @@ -625,6 +625,7 @@ typedef struct MessageUpdTSOFlag_ { |
| 625 | 625 | Message *link;
|
| 626 | 626 | StgTSO *tso;
|
| 627 | 627 | StgWord flag;
|
| 628 | + StgWord set; // bool: true=SET; false=UNSET
|
|
| 628 | 629 | } MessageUpdTSOFlag;
|
| 629 | 630 | |
| 630 | 631 | /* ----------------------------------------------------------------------------
|
| ... | ... | @@ -151,8 +151,7 @@ RTS_ENTRY(stg_MSG_TRY_WAKEUP); |
| 151 | 151 | RTS_ENTRY(stg_MSG_THROWTO);
|
| 152 | 152 | RTS_ENTRY(stg_MSG_BLACKHOLE);
|
| 153 | 153 | RTS_ENTRY(stg_MSG_CLONE_STACK);
|
| 154 | -RTS_ENTRY(stg_MSG_SET_TSO_FLAG);
|
|
| 155 | -RTS_ENTRY(stg_MSG_UNSET_TSO_FLAG);
|
|
| 154 | +RTS_ENTRY(stg_MSG_UPD_TSO_FLAG);
|
|
| 156 | 155 | RTS_ENTRY(stg_MSG_NULL);
|
| 157 | 156 | RTS_ENTRY(stg_MVAR_TSO_QUEUE);
|
| 158 | 157 | RTS_ENTRY(stg_catch);
|