Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
5b550754
by Matthew Pickering at 2026-03-26T03:54:51-04:00
5 changed files:
- rts/CloneStack.c
- rts/CloneStack.h
- rts/Messages.c
- testsuite/tests/rts/all.T
- + testsuite/tests/rts/cloneThreadStackMigrating.hs
Changes:
| ... | ... | @@ -84,15 +84,31 @@ void sendCloneStackMessage(StgTSO *tso, HsStablePtr mvar) { |
| 84 | 84 | sendMessage(srcCapability, tso->cap, (Message *)msg);
|
| 85 | 85 | }
|
| 86 | 86 | |
| 87 | -void handleCloneStackMessage(MessageCloneStack *msg){
|
|
| 88 | - StgStack* newStackClosure = cloneStack(msg->tso->cap, msg->tso->stackobj);
|
|
| 87 | +// The cap argument is the capability which is handling the CloneStack message
|
|
| 88 | +void handleCloneStackMessage(Capability *cap, MessageCloneStack *msg){
|
|
| 89 | + // We must check that the current owner of the thread we want to clone the stack for
|
|
| 90 | + // is still this capability.
|
|
| 91 | + Capability *owner = RELAXED_LOAD(&msg->tso->cap);
|
|
| 92 | + if (owner != cap) {
|
|
| 93 | + // The target TSO may have migrated after the message was queued on the old
|
|
| 94 | + // capability. In that case we must forward the request to the current
|
|
| 95 | + // owner; otherwise we would race with another capability mutating the
|
|
| 96 | + // stack while we clone it.
|
|
| 97 | + sendMessage(cap, owner, (Message *)msg);
|
|
| 98 | + return;
|
|
| 99 | + }
|
|
| 100 | + |
|
| 101 | + // At this point the executing capability owns the TSO, so it is the only
|
|
| 102 | + // capability that may safely inspect the live stack and the one whose
|
|
| 103 | + // allocator we must use for the cloned StgStack closure.
|
|
| 104 | + StgStack* newStackClosure = cloneStack(cap, msg->tso->stackobj);
|
|
| 89 | 105 | |
| 90 | 106 | // Lift StackSnapshot# to StackSnapshot by applying it's constructor.
|
| 91 | 107 | // This is necessary because performTryPutMVar() puts the closure onto the
|
| 92 | 108 | // stack for evaluation and stacks can not be evaluated (entered).
|
| 93 | - HaskellObj result = rts_apply(msg->tso->cap, StackSnapshot_constructor_closure, (HaskellObj) newStackClosure);
|
|
| 109 | + HaskellObj result = rts_apply(cap, StackSnapshot_constructor_closure, (HaskellObj) newStackClosure);
|
|
| 94 | 110 | |
| 95 | - bool putMVarWasSuccessful = performTryPutMVar(msg->tso->cap, msg->result, result);
|
|
| 111 | + bool putMVarWasSuccessful = performTryPutMVar(cap, msg->result, result);
|
|
| 96 | 112 | |
| 97 | 113 | if(!putMVarWasSuccessful) {
|
| 98 | 114 | barf("Can't put stack cloning result into MVar.");
|
| ... | ... | @@ -17,7 +17,7 @@ void sendCloneStackMessage(StgTSO *tso, HsStablePtr mvar); |
| 17 | 17 | #include "BeginPrivate.h"
|
| 18 | 18 | |
| 19 | 19 | #if defined(THREADED_RTS)
|
| 20 | -void handleCloneStackMessage(MessageCloneStack *msg);
|
|
| 20 | +void handleCloneStackMessage(Capability *cap, MessageCloneStack *msg);
|
|
| 21 | 21 | #endif
|
| 22 | 22 | |
| 23 | 23 | #include "EndPrivate.h" |
| ... | ... | @@ -135,7 +135,7 @@ loop: |
| 135 | 135 | }
|
| 136 | 136 | else if(i == &stg_MSG_CLONE_STACK_info){
|
| 137 | 137 | MessageCloneStack *cloneStackMessage = (MessageCloneStack*) m;
|
| 138 | - handleCloneStackMessage(cloneStackMessage);
|
|
| 138 | + handleCloneStackMessage(cap, cloneStackMessage);
|
|
| 139 | 139 | }
|
| 140 | 140 | else
|
| 141 | 141 | {
|
| ... | ... | @@ -587,6 +587,15 @@ test('cloneMyStack_retBigStackFrame', [req_c, extra_files(['cloneStackLib.c']), |
| 587 | 587 | |
| 588 | 588 | test('cloneThreadStack', [req_c, only_ways(['threaded1']), extra_ways(['threaded1']), extra_files(['cloneStackLib.c']), req_ghc_with_threaded_rts], compile_and_run, ['cloneStackLib.c -threaded'])
|
| 589 | 589 | |
| 590 | +test('cloneThreadStackMigrating',
|
|
| 591 | + [ ignore_stdout
|
|
| 592 | + , only_ways(['threaded1'])
|
|
| 593 | + , extra_ways(['threaded1'])
|
|
| 594 | + , extra_run_opts('+RTS -N -DS -RTS')
|
|
| 595 | + , req_ghc_with_threaded_rts
|
|
| 596 | + , req_target_smp
|
|
| 597 | + ], compile_and_run, ['-threaded -debug -rtsopts'])
|
|
| 598 | + |
|
| 590 | 599 | test('decodeMyStack',
|
| 591 | 600 | [ omit_ghci, js_broken(22261) # cloneMyStack# not yet implemented
|
| 592 | 601 | , when(ghc_with_ipe(), skip) # IPE builds can change decoded stack output.
|
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Control.Concurrent
|
|
| 4 | +import Control.Monad
|
|
| 5 | +import GHC.Exts.Stack
|
|
| 6 | +import GHC.Stack.CloneStack
|
|
| 7 | + |
|
| 8 | +numWorkers :: Int
|
|
| 9 | +numWorkers = 100
|
|
| 10 | + |
|
| 11 | +startN :: Int
|
|
| 12 | +startN = 10
|
|
| 13 | + |
|
| 14 | +runForMicros :: Int
|
|
| 15 | +runForMicros = 1000000
|
|
| 16 | + |
|
| 17 | +fib :: Int -> Int
|
|
| 18 | +fib 0 = 1
|
|
| 19 | +fib 1 = 1
|
|
| 20 | +fib n = fib (n - 1) + fib (n - 2)
|
|
| 21 | + |
|
| 22 | +workerThread :: Int -> IO ()
|
|
| 23 | +workerThread n = do
|
|
| 24 | + fib n `seq` pure ()
|
|
| 25 | + workerThread (n + 1)
|
|
| 26 | + |
|
| 27 | +cloneThread :: ThreadId -> IO ()
|
|
| 28 | +cloneThread tid = forever $ do
|
|
| 29 | + snapshot <- cloneThreadStack tid
|
|
| 30 | + stack <- decodeStack snapshot
|
|
| 31 | + stack `seq` pure ()
|
|
| 32 | + |
|
| 33 | +main :: IO ()
|
|
| 34 | +main = do
|
|
| 35 | + tids <- replicateM numWorkers (forkIO $ workerThread startN)
|
|
| 36 | + mapM_ (forkIO . cloneThread) tids
|
|
| 37 | + threadDelay runForMicros |