Repository navigation
Conversation
Use a single suspension attempt for asynchronous VM trap sampling while preserving blocking suspension for mandatory GC snapshots. Cover foreign-stack refusal, nested suspension, parent collection progress, and pending termination delivery in a native regression.
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.
What does this PR do?
Avoid a Linux deadlock when a Worker is terminated while a native call is running on a foreign stack and waiting for another VM. The asynchronous trap sender currently calls blocking
Thread::suspend()under the process-wide suspension lock. A stack pointer outside the registered thread stack causes that call to retry indefinitely; another VM cannot collect, so it cannot run the callback that lets the native call return.Give optional VM-trap sampling a single suspension attempt. A foreign-stack refusal leaves the trap pending and lets the existing work-queue retry release the suspension lock first. Mandatory GC suspension still waits for a valid register snapshot. Failed attempts neither invoke the inspection callback nor change suspension nesting, and platform errors are not reported as thread exit. Mach suspension retains its existing operation.
The native regression enters a VM-owned host function on a guarded custom stack, requests termination, waits for the trap suspension signal, and requires parent full GC to finish before releasing the native call. It also checks protected-root survival, canonical termination delivery, temporary refusal, later delivery, and nested suspension/resume.
Related: #235, #741, #742, and WebKit/WebKit#65562 (bug 315453) address alternate signal-stack register selection. They do not cover a thread whose interrupted stack pointer is genuinely outside its registered stack. General same-VM foreign-stack GC scanning and the sampling profiler's blocking sampling path are outside this fix.
Adapted from my fixes in openclaw/WebKit#25 and #30.
How did you verify your code works?
Fresh Linux x86-64 builds based on
mainat5d2ceba2921d29312827883b1fafa5c7cd5cba7e, using LLVM 23.1.2, Release-O2 -g1, static JSC, native LLInt, Bun additions/event loop, FTL, and external mimalloc. Both arms used identical engine configuration; the candidate applied only this PR's six production-file changes.For the unchanged-main control, the same regression was compiled with
TEST_FOREIGN_STACK_BLOCKING_CONTROL=1. This omits only the newtrySendMessageAPI checks, which cannot compile against main; the termination/GC test body is identical. A single run under GDB reached its existing 30-second alarm. Its all-thread capture showed the trap sender inThread::suspend()→sendMessageScoped()→SignalSender::work(), the other VM's full GC waiting inMachineThreads::tryCopyOtherThreadStacks()on the suspension lock, and the native function still on its foreign stack. The debugger confirmedSIGALRM(14) and returned 124 for that expected negative control.With this patch and the control define removed, the regression exited 0:
The baseline and candidate shared test/build harness additions; no other production fix was present in either arm. This is native Linux x86-64 proof, not a new sanitizer or cross-platform run. WebKit style checks pass for all eight changed files, and
git diff --checkis clean.Debugger setup for the unchanged-main control
Run
gdb --batch --nx -x baseline.gdb --args build/bin/testForeignStackSuspension, withbaseline.gdbcontaining: