Skip to content

fix(test262): stop in-process collectible-ALC churn crashing the testhost (#964)#965

Merged
nickna merged 1 commit into
mainfrom
wrk/issue-964-test262-host-crash
Jun 27, 2026
Merged

fix(test262): stop in-process collectible-ALC churn crashing the testhost (#964)#965
nickna merged 1 commit into
mainfrom
wrk/issue-964-test262-host-crash

Conversation

@nickna

@nickna nickna commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #964. The unfiltered dotnet test SharpTS.Test262 crashed the test host with a fatal CLR error — Internal CLR error. (0x80131506) (COR_E_EXECUTIONENGINE) — before the compiled differ ran, so compiled-mode Test262 conformance couldn't be measured via the convenience run. --filter ~CompiledBaseline / ~InterpretedBaseline completed, which made it look "combined-run-only."

Root cause

The crash has nothing to do with the compiled CompiledBaseline differ, #882, or codegen (#963). Both baselines run through the subprocess worker pool, which uses the non-collectible Assembly.Load(byte[]) path and never Unload()s — a guest fault there is contained in a child process.

The only in-process emitted-IL execution in the test host is SmokeTest: its ~20 diagnostic facts each run compiled tests through a collectible AssemblyLoadContext (load → JIT → Invoke → Unload()), plus a periodic forced GC.Collect(), all under ServerGarbageCollection + ConcurrentGarbageCollection. That collectible-ALC teardown churn intermittently hits the fatal engine fault while JIT-compiling a collectible assembly's entry point.

Proven by dump + isolation:

  • --filter ~SmokeTest alone (no baselines present) reproduces the identical crash at ~1m25s → the crash is selection-based, not concurrency-based.
  • Dump faulting frame: [PrestubMethodFrame] $Program.Main() (JIT of a collectible assembly) on the InvokeCompiledMain Task.Run worker.
  • ~CompiledBaseline "completes" only because it excludes SmokeTest.

This is a known .NET runtime fragility around collectible-ALC unload under JIT + concurrent GC (cf. dotnet/runtime#34838, #40547, #45285; MS Learn unloadability docs note unload is "cooperative," the JIT can hold hidden refs, and R2R is ignored → everything JITs). It is not a SharpTS logic bug — the same emitted IL runs cleanly in the workers and in standalone DLLs.

Fix

  • Test262Runner.cs: UseNonCollectibleLoad changed from a racy mutable process-global static to an immutable per-instance property, set via a new optional ctor parameter (useNonCollectibleLoad = false). Per-instance so concurrently-running xUnit collections can't clobber each other's mode.
  • SmokeTest.cs: all in-process runners opt into useNonCollectibleLoad: true (curated lists are small → per-process leak negligible). Diagnostic_CompiledPhaseBreakdown rewritten to build a per-mode runner and skipped, since its "collectible" arm intentionally drives the crash path.
  • Worker/Program.cs: passes the flag via the ctor (behavior-identical — workers were already non-collectible).
  • Test262Tests.cs: documents that the in-process baseline fallback (worker-not-found, ~11k tests) deliberately stays collectible to avoid the Test262 #10: Compile-mode regen accumulates dynamic assemblies — per-test isolation needed #109 28 GB OOM.

Validation

Run Before After
--filter ~SmokeTest crash @ ~1m25s 20 pass / 4 skip, clean
Full unfiltered dotnet test crash @ ~1m46s, differ never ran completes 3m17s, no 0x80131506, compiled differ runs 11384/11384 (0 regressions, 8 new passes)

The remaining Failed: 2 in the full run are the pre-existing baseline-drift assertions (interpreted 15 regressions / 49 new passes — timeout flakiness; compiled 8 new passes — the same 8 reported in #882's comment). That is separate #882-family staleness and is out of scope for this fix.

…host (#964)

The unfiltered `dotnet test SharpTS.Test262` crashed the test host with a
fatal CLR error (`Internal CLR error. (0x80131506)`, COR_E_EXECUTIONENGINE)
before the compiled differ could run, so compiled-mode Test262 conformance
was unmeasurable via the convenience run. `--filter ~CompiledBaseline` /
`~InterpretedBaseline` completed, which made it look combined-run-only.

Root cause: the only in-process emitted-IL execution in the test host is
SmokeTest, whose ~20 diagnostic facts run compiled tests through a
collectible AssemblyLoadContext (load -> JIT -> Invoke -> Unload) plus a
periodic forced GC, all under Server + Concurrent GC. That collectible-ALC
teardown churn intermittently hits the fatal engine fault while JIT-compiling
a collectible assembly's entry point (dump faulting frame:
`[PrestubMethodFrame] $Program.Main()`). Proven by `--filter ~SmokeTest`
crashing on its own with no baselines present — so it is selection-based, not
concurrency-based. Both baselines are immune: they run through the subprocess
worker pool, which uses the non-collectible `Assembly.Load(byte[])` path and
never Unloads.

This is a known .NET runtime fragility around collectible ALC unload under
JIT + concurrent GC (cf. dotnet/runtime #34838, #40547, #45285), not a SharpTS
logic bug — the same emitted IL runs cleanly in the workers and standalone.

Fix: make `Test262Runner.UseNonCollectibleLoad` a per-instance immutable
property (was a racy mutable process-global static) set via a new optional
ctor parameter. SmokeTest opts all its in-process runners into the
non-collectible path (its curated lists are small, so the per-process leak is
negligible); the worker passes the flag via the ctor (behavior-identical).
`Diagnostic_CompiledPhaseBreakdown` is rewritten to build a per-mode runner
and skipped, since its "collectible" arm intentionally drives the crash path.
The in-process baseline fallback (worker-not-found, ~11k tests) deliberately
stays collectible to avoid the issue #109 OOM, now documented.

Validation:
- `--filter ~SmokeTest`: was crash @ ~1m25s -> 20 pass / 4 skip, clean.
- Full unfiltered run: was crash @ ~1m46s (differ never ran) -> completes in
  3m17s, no 0x80131506, compiled differ runs to the end (11384/11384,
  0 regressions / 8 new passes).

The remaining baseline drift (interpreted 15 regressions/49 new passes,
compiled 8 new passes) is pre-existing #882-family staleness, out of scope.
@nickna nickna merged commit 286f2a9 into main Jun 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test262: full-subset dotnet test crashes the test host (Internal CLR error 0x80131506) before the compiled differ runs

1 participant