Skip to content

[browser][coreCLR] corerun: hard-exit Node on explicit process exit (#131937) - #132078

Merged
pavelsavara merged 2 commits into
dotnet:mainfrom
pavelsavara:fix-131937-corerun-node-exit
Aug 10, 2026
Merged

[browser][coreCLR] corerun: hard-exit Node on explicit process exit (#131937)#132078
pavelsavara merged 2 commits into
dotnet:mainfrom
pavelsavara:fix-131937-corerun-node-exit

Conversation

@pavelsavara

Copy link
Copy Markdown
Member

Problem

JIT/Regression/CLR-x86-JIT/V1-M12-Beta2/b65423 fails on the browser-wasm CoreCLR checked leg (#131937). The test catches its expected TypeLoadException (the intended Expected: 100), but the wasm runtime then aborts during shutdown:

Caught TypeLoadException exception: 100
Aborted(Assertion failed: native function `strerror` called after runtime exit
(use NO_EXIT_RUNTIME to keep it alive after main() exits))
... at _fd_write (corerun.js)
Expected: 100
Actual: 1

Root cause

The test ends with Environment.Exit(100). On browser-wasm that goes Environment_ExitForceEEShutdownEEPolicy::HandleExitProcessSafeExitProcessExitProcessPROCEndProcess → Emscripten exit() (the host links -sEXIT_RUNTIME=1).

Emscripten's exitJS runs exitRuntime() — tearing down the Emscripten FS/TTY and setting runtimeExited=true — then _proc_exit throws the ExitStatus JS exception. That exception unwinds up through the still-live CLR interpreter frames (wasm EH). During the unwind, Frame::Pop(Thread*) calls Thread::GetFrame(), whose checked-only _ASSERTE (curSP <= m_pFrame && m_pFrame < m_CacheStackBase) fails because we are exiting mid-interpretation. The assert's fprintf(stderr, ...) then hits musl stdio → fd_write → the FS streams are already gone → getStreamChecked throws ErrnoError, and building its message calls strerror, tripping Emscripten's assert(!runtimeExited)abort(). The abort turns the intended exit code 100 into 1.

This only reproduces on the checked leg because the triggering GetFrame() assert exists only under _DEBUG_IMPL.

Fix

In the corerun wasm glue, under Node, intercept _proc_exit and end the process immediately with process.exit(code) when the runtime is not kept alive — avoiding the ExitStatus throw and the unwind through live CLR frames. stdio was already flushed by exitRuntime() before _proc_exit is reached. The browser/keepalive/onExit path is untouched (Node-gated), and when the runtime is kept alive for async work it falls through to the original behavior.

_proc_exit is the correct interception point: wasmImports.exit binds the original exitJS by value (so reassigning exitJS would miss the wasm-initiated Environment.Exit), but exitJS's body calls _proc_exit by variable reference at call time.

Validation

Ran the assembled b65423.dll under corerun.js in Node, exactly as CI does:

corerun.jsNode exit
before fix1 (strerror abort — matches the issue)
after fix100 (expected)

Regression checks also pass: normal return 7 from Main → exit 7; Environment.Exit(0) → exit 0.

Fixes#131937

Note

This PR description was generated with the assistance of GitHub Copilot.

Environment.Exit reaches _proc_exit from deep inside interpreted managed code. Letting Emscripten throw ExitStatus and unwind back through the live CLR interpreter frames runs native code (a checked-build Frame::Pop assert, stdio) after the runtime/FS teardown, aborting with 'strerror called after runtime exit' and turning the intended exit code into 1 (dotnet#131937).
Under Node, intercept _proc_exit and process.exit(code) immediately when the runtime is not kept alive, avoiding the unwind. stdio was already flushed by exitRuntime().
Fixesdotnet#131937
CopilotAI lite review requested due to automatic review settings August 10, 2026 13:46
@azure-pipelines

Copy link
Copy Markdown
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.

@pavelsavarapavelsavara added arch-wasm WebAssembly architecture os-browser Browser variant of arch-wasm labels Aug 10, 2026
@pavelsavarapavelsavara added this to the 11.0.0 milestone Aug 10, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a browser-wasm CoreCLR checked-leg test failure where Environment.Exit(...) triggers an Emscripten ExitStatus throw that unwinds through live interpreter frames and aborts with the wrong exit code under Node.

Changes:

  • Under Node, intercept _proc_exit to hard-exit via process.exit(code) when the runtime is not being kept alive.
  • Preserve existing behavior for non-Node environments and for keepalive/async scenarios by delegating to the original _proc_exit.

Comment threadsrc/coreclr/hosts/corerun/wasm/libCorerun.js
CopilotAI review requested due to automatic review settings August 10, 2026 13:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/hosts/corerun/wasm/libCorerun.js:43

  • Local variable original_proc_exit uses snake_case, which is inconsistent with the surrounding camelCase naming in this file. Consider renaming to originalProcExit while keeping the Emscripten-provided _proc_exit identifier as-is.
 const original_proc_exit = _proc_exit;
_proc_exit = (code) => {
if (!keepRuntimeAlive()) {
process.exit(code);
}
return original_proc_exit(code);
};

@pavelsavara
pavelsavara merged commit 28c5e52 into dotnet:mainAug 10, 2026
130 checks passed
@pavelsavara
pavelsavara deleted the fix-131937-corerun-node-exit branch August 10, 2026 16:38
@dotnet-milestone-botdotnet-milestone-botBot modified the milestones: 11.0.0, 11.0-rc1Aug 11, 2026
radekdoulik added a commit to radekdoulik/runtime that referenced this pull request Aug 18, 2026
Remove the b65423 and fieldlayout suppressions now that dotnet#132078 and
dotnet#132172 fix their underlying CoreCLR browser failures.
Retain the EventPipe, ContextualReflection, and Server GC exclusions
that remain necessary on current main.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 808c6867-a1ea-4256-a003-46c17785ad04
radekdoulik added a commit that referenced this pull request Aug 19, 2026
)
## Summary
- Gate exactly four EventPipe events-validation projects on CoreCLR
browser because they use unsupported `Process.GetCurrentProcess` and
Process/PID-based managed self-process `DiagnosticsClient` collection;
browser diagnostics use JS/WebSocket clients instead.
- Temporarily suppress the CoreCLR-browser `ContextualReflection`
interpreter failure through its tracked #131925 ActiveIssue.
- Clarify that Server GC is disabled on CoreCLR browser by design.
- `b65423` and `readytorun/fieldlayout` are absent from this change:
current main includes the underlying fixes from #132078 and #132172, and
both restored tests now pass targeted browser validation.
Related issues: #131321, #131898, #131925.
## Validation
- Merged and validated current `origin/main` at
`663c457b86ccd35743374676a69de5ed97bc3b14`; the Checked browser
`clr+libs` baseline passed with 0 warnings and 0 errors.
- `b65423` passed 3/3: browser default, browser
`DOTNET_TieredCompilation=0`, and native macOS arm64 process-isolated
execution, each with expected/actual exit code 100.
- `fieldlayout` passed 2/2 in browser default and no-tiered modes after
producing and loading its composite Crossgen2/R2R WASM image.
- Removing the ContextualReflection ActiveIssue reproduced #131925 in
2/2 browser modes; the final gated browser runs passed 2/2, and native
ContextualReflection executed successfully.
- An ungated no-tiered `GCEvents` run reproduced the expected
`Process.GetCurrentProcess` PlatformNotSupportedException; all four
final EventPipe projects were gated from CoreCLR-browser builds.
- 14/14 representative property evaluations matched the intended scope
across CoreCLR browser, CoreCLR macOS, and browser Mono.
- Final delta: exactly 6 files, 23 insertions, 3 deletions. `git diff
--check origin/main..HEAD` passed.
> [!NOTE]
> This pull request description was generated by GitHub Copilot.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 808c6867-a1ea-4256-a003-46c17785ad04
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasmWebAssembly architecturearea-Hostos-browserBrowser variant of arch-wasm

Projects

None yet

3 participants

@pavelsavara@maraf