Repository navigation
Do not adjust wasm virtual IPs in the thread abort stack walk - #135915
Merged
Merged
Conversation
On wasm, the control PC of a ReadyToRun frame is a virtual IP: the JIT numbers the regions of a method (fgWasmVirtualIP) and stores the number of the current region in the frame before each call. It is not a return address. TAStackCrawlCallBackWorker moves the offset of every non-active frame back by one before comparing it with the EH clauses, so a call in the first region of a finally or catch handler was treated as being just before the handler. Thread::ReadyForAsyncException then let a thread abort be raised inside the handler. Skip the adjustment for virtual IPs, as SfiInit and SfiNext already do.
ControlledExecutionTests was conditioned on IsMultithreadingSupported as a whole, so none of its tests ran on the single-threaded wasm legs. Only the tests that cancel from Task.Run need a second thread; move the condition to them. CancelBeforeRun then covers the wasm ReadyToRun case in which the abort was raised again inside ControlledExecution.Run's catch and finally.
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
This was referenced Oct 16, 2026
Closed
Member
|
/azp run runtime-coreclr outer loop |
|
No pipelines are associated with this pull request. |
lewing
approved these changes
Oct 17, 2026
Member
|
thanks for tracking down the misclassification here |
This was referenced Oct 17, 2026
Contributor
Author
|
@jkotas lewing approved this, but Build Analysis is still red from the run on October 16. The check summary shows these unmatched failures:
Would you prefer a rerun, or |
Contributor
Author
|
Thank you for the review and the merge. |
pcshrosbree
added a commit
to pcshrosbree/runtime
that referenced
this pull request
Oct 19, 2026
The previous commit enables this test again. Since dotnet#135915 removed the class-level IsMultithreadingSupported condition from ControlledExecutionTests, the test now also runs on single-threaded browser-wasm CoreCLR. There, with the default ReadyToRun CoreLib, an action that cancels itself still leaves the abort pending at the end of CancellationTokenSource's ReadyToRun catch (dotnet#135914). Checked runtimes assert there. Skip the test on browser CoreCLR until dotnet#135914 is fixed.
This was referenced Oct 19, 2026
lewing
pushed a commit
that referenced
this pull request
Oct 20, 2026
…ormally (#136116) > **TL;DR:** Since #135939 merged, `System.Runtime.Tests` times out in browser-wasm CI. The likely cause is a test that needs threads but now runs on single-threaded wasm, where it cannot finish. This one-line change skips that test, `ControlledExecutionTests.CancelInTryAndExitCatchNormally`, when threads are unavailable, as the other `Task.Run` tests in its class do. This PR's own browser CI run will show whether that resolves the timeout. The test cancels its action from a `Task.Run` task while the action spins. #135915 and #135939 merged a minute apart, and each changed this class: - #135915 moved `IsMultithreadingSupported` from the class onto the four enabled tests that cancel from `Task.Run`. - #135939 enabled `CancelInTryAndExitCatchNormally` again, but its branch predated #135915, so on main the test has no such condition. On single-threaded wasm, the canceling task cannot run while the action spins. In the browser-wasm `LibraryTestsCoreCLR` leg, `System.Runtime.Tests` had these results: - Without #135939: passed in 872 s (build [1641014](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1641014)) and 972 s (build [1641108](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1641108)). - With #135939: timed out at 5402 s in both builds [1641112](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1641112) and [1641117](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1641117). Under `src/libraries`, #135939 changed only this test's attribute. The log does not name the test that was running. Both PRs are mine, and I missed the interaction before they merged. > [!NOTE] > This PR was prepared with AI assistance (Anthropic Claude, checked by OpenAI Codex) at my request. I reviewed it before posting it.
pcshrosbree
added a commit
to pcshrosbree/runtime
that referenced
this pull request
Oct 29, 2026
The previous commit enables this test again. Since dotnet#135915 removed the class-level IsMultithreadingSupported condition from ControlledExecutionTests, the test now also runs on single-threaded browser-wasm CoreCLR. There, with the default ReadyToRun CoreLib, an action that cancels itself still leaves the abort pending at the end of CancellationTokenSource's ReadyToRun catch (dotnet#135914). Checked runtimes assert there. Skip the test on browser CoreCLR until dotnet#135914 is fixed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thread::ReadyForAsyncExceptionrefuses to raise a thread abort inside afinallyorcatchhandler. Its walk,TAStackCrawlCallBackWorker, moves the offset of every non-active frame back by one before comparing it with the EH clauses, because the frame's control PC is a return address.On wasm, the control PC of a ReadyToRun frame is a virtual IP instead.
fgWasmVirtualIPgives each region of a method a number and stores the current region's number in the frame before each call. A new region starts at every try, handler and filter boundary, so every call in the first region of a handler reports exactly the handler's start. Moved back by one, that is the previous region, outside the handler. With the default ReadyToRun CoreLib,ControlledExecution.Runthen gets the abort raised again inside its ownfinallyandcatch (ThreadAbortException)handlers, at their first call through an import cell, and throwsThreadAbortExceptioninstead ofOperationCanceledException.SfiInitandSfiNextalready skip the adjustment for virtual IPs. This change does the same inTAStackCrawlCallBackWorker. A virtual IP never needs the adjustment: it names the region of the call in progress, and EH boundaries are region boundaries. For this walk, which checks only handler ranges, the adjustment changes the answer only at a handler's first region, where it is wrong; crossing a try boundary does not change handler membership. Active frames were never adjusted. Interpreter frames are not virtual IPs and keep the adjustment.The second commit lets the
ControlledExecutionTeststhat need no second thread run on the single-threaded wasm legs. The whole class was conditioned onIsMultithreadingSupported, so none of it ran there. The condition is now on the four tests that cancel fromTask.Run.CancelBeforeRuncovers this fix. #135520 re-enablesCancelInTryAndExitCatchNormally, which also usesTask.Run; whichever of the two lands second needs to add the condition to it.Measured with browser-wasm Checked builds under node, single-threaded, default ReadyToRun CoreLib, using a program with the same shapes as the tests:
3994df1ceee1CancelBeforeRun(token cancelled beforeRun)OperationCanceledExceptionCanceler.CancelThreadAbortExceptionOperationCanceledExceptiontry/finally, sameThreadAbortException, finally ranOperationCanceledException, finally ranCancelItselfFromFinallyDOTNET_ReadyToRun=0On linux-x64, osx-arm64 and windows-x64 (Checked), the runtime's code differs from main only in line-number constants after the change. On linux-x64, restoring the original line numbers makes the code identical to main. On osx-arm64 and windows-x64, it is identical to a control with the wasm-only lines removed and their line positions retained.
ControlledExecutionTestspasses 8/8 on all three with the test change. I did not run the library tests on wasm locally or test a Release runtime.This fixes the first problem in #135914. Token cancellation from inside
Runstill reaches the second problem: after CTS's ReadyToRun catch, the end-of-catch abort check asserts on wasm in Checked builds. I'm asking there how you'd like it done.Partially addresses #135914 (the second problem stays open there).
Note
This PR was prepared with AI assistance (Anthropic Claude, checked by OpenAI Codex) at my request. I reviewed the change, the test and the measurements before opening it.