Repository navigation
fix(jsc): retry traps after foreign-stack suspension refusal - #25
Merged
steipete merged 6 commits intoOct 9, 2026
Merged
Conversation
steipete
marked this pull request as ready for review
October 9, 2026 19:29
This was referenced Oct 9, 2026
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.
A Worker blocked in a native call on a foreign stack can deadlock every other VM's collector on Linux. The asynchronous trap sender holds WTF's process-wide suspension lock while
Thread::suspend()retries an out-of-bounds stack-pointer refusal indefinitely. If the native call waits for the parent, and parent GC needs that lock before running its release callback, none can progress.Add a distinct temporary-refusal result for one suspension attempt and use it only for asynchronous VM trap sampling. The existing work-queue retry releases the lock first; termination and stop-the-world traps remain pending. Blocking conservative GC suspension still requires a valid snapshot. Mach suspension retains its existing operation. Failed attempts do not invoke the inspection callback, change suspension nesting, or classify a live thread as exited.
The native Linux regression parks a VM-owned host function on a guarded custom stack, requests termination, observes the trap suspension signal, and requires parent full GC to finish before releasing the callback. It checks protected-root survival, canonical termination delivery, temporary refusal, later delivery, and nested suspension/resume. The Linux engine artifact check runs it. General same-VM foreign-stack scanning and the sampling profiler's separate blocking path remain outside this change.
Validation
Each Koffi process performs ten sequential Worker handshakes and retains its 120-second deadline. All arms use the same fixed schedule: 33 sequential processes, then 167 at four concurrent processes. All completed observations were retained. One baseline timeout includes controller-transfer overhead and is not used as timing evidence. The prebuilt baseline and rebuilt candidate differ in compiler settings; the native before/after pair uses identical compiler settings and supplies the controlled causal comparison.
Fresh symbolized captures use the matching released runtime/profile ELF build ID
cfda5bddca52aafc395a18d608a9abadcbc82afe. A separate bounds diagnostic places the blocked Worker SP at0x766e380ffe78, outside its registered stack(0x766e39bff000, 0x766e39fff000].TSAN initially could not enter the test under Docker's default ASLR restriction. Running the identical binary with process-local ASLR disabled exposed eight unique report signatures in existing GC/thread code. A separately rebuilt unpatched engine running ordinary two-VM full GC, without traps or foreign stacks, reproduced all eight signatures (66 reports, exit 66). No suppressions, reduced GC concurrency, or timeout changes were used. This establishes baseline attribution for the observed reports, not general race freedom. Both nonzero results remain recorded.
P2 scoped review is clean; WebKit style reports 0 errors across all ten changed files. Exact-head checks: engine checks, Linux ARM64. All five checks passed on the final combined head. Linux paired qualification passed 46/46 result entries in each arm, sync passed 85/85, and both Linux architectures passed 1,779 stress and 1,639 module results. The native suspension regression passed in the final Linux build. The 600-process matrix and paired native/sanitizer proof were collected at isolated head
346ba3317411e1cc5967b0d3f5b7d70e5a290d13. Integration head436d2b3058105c76334391c65c085b691c024c91adds the landed WebKit#24 release base; all six suspension/trap source files and the native regression are byte-identical. The only manual merge resolution preserves both sets of CMake test targets. Final CI passed against that combined source state.Upstream and integration
Related upstream work: WebKit#65562 (bug 315453) and oven-sh#235, oven-sh#741, oven-sh#742 handle alternate signal stacks or saved contexts in WTF signal handlers. Those still reject an interrupted SP genuinely on an unregistered foreign stack, such as Koffi's assembly stack switch; none supplies this best-effort trap-sampling contract.
This draft targets
openclaw/release-5718a6ec. The production patch applies toopenclaw/mainatfbc05476b051c7d65d6f2c333f8705489353d6a8; that port still needs equivalent regression/build wiring. No Bun runtime adapter change is needed. The integration artifact is unpublished, and no consumer pin, merge or release is included.