Skip to content

[Host/Signals] Deliver caught signals before retrying waits - #1129

Open
brandonpayton wants to merge 1 commit into
mainfrom
fix/retried-signal-delivery-qk044
Open

[Host/Signals] Deliver caught signals before retrying waits#1129
brandonpayton wants to merge 1 commit into
mainfrom
fix/retried-signal-delivery-qk044

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Why

A caught signal can arrive while the host is retrying a blocking
syscall. Rust dequeues that signal into the process channel before the
host inspects the kernel's internal EAGAIN result.

The old retry path could park the same channel again. The guest could
not run the handler until the mailbox completed, and the next retry
could clear the channel's only signal record. In the WordPress mail
path, msmtpd lost SIGCHLD, retained zombie session children, filled
its 16-session limit, and stopped greeting new clients.

The hard part is that EAGAIN has two meanings at this boundary. It can
be a public result from a nonblocking operation, or an internal request
for the host to keep waiting. Signal delivery must interrupt only the
latter.

What changed

  • Treat a populated CH_SIG record as channel-owned until libc consumes
    it.
  • Before any shared blocking retry parks, publish EINTR with that
    record intact so libc can run the handler.
  • Preserve public EAGAIN for nonblocking descriptors,
    MSG_DONTWAIT, and zero-time waits.
  • Apply the same rule to host-managed pending network connects.
  • Transparently restart accept and accept4 only when the caught
    handler uses SA_RESTART. Those calls are safe to resubmit because
    the interrupted attempt did not remove a connection from the queue.
  • Document the signal ownership and retry contract.

The state machine lives in shared CentralizedKernelWorker code, so
Node.js and browser hosts use the same behavior.

Validation

  • scripts/dev-shell.sh bash scripts/build-musl.sh
  • scripts/dev-shell.sh npm --prefix host run build
  • Focused Vitest coverage: 7 files, 78 tests passed.
  • A real guest regression forks a child, catches SIGCHLD while blocked
    in accept, and verifies both observable EINTR and SA_RESTART.
  • scripts/dev-shell.sh bash scripts/check-abi-version.sh

The ABI snapshot remains unchanged.

The local Sortix signal/process run reached all 56 cases but protected
main's stale program-package projection rejected every case before
guest startup. Browser execution was not run locally; CI remains the
browser evidence for this shared host-runtime change.

Treat CH_SIG as channel-owned state after the kernel dequeues a
caught signal. A blocking host retry now completes with EINTR before it
can re-park, so libc can run the handler instead of losing the only
signal record.

Keep public nonblocking EAGAIN outcomes intact and narrowly restart
accept and accept4 when SA_RESTART applies. Cover the shared retry state
machine, pending connect, and the real fork/SIGCHLD/accept guest path.
brandonpayton added a commit that referenced this pull request Aug 12, 2026
Treat CH_SIG as channel-owned state after the kernel dequeues a
caught signal. A blocked host retry now completes with EINTR before it
can re-park, so libc can run the handler without losing the only signal
record.

Forward-port #1129 onto ABI 43 exact retry ownership. Host-generated
events now enter through an additive kernel export instead of borrowing
the target task's occupied syscall channel. Preserve public nonblocking
EAGAIN, SA_RESTART policy, and Node/browser parity.

Validation:
- 1,500 native kernel tests
- 177 focused blocking-retry and signal tests
- real accept/SIGCHLD guest in Node.js
- real accept/SIGCHLD guest in Chromium, Firefox, and WebKit
- ABI snapshot, host typecheck, and documentation build

(cherry picked from commit 997fc7b)
brandonpayton added a commit that referenced this pull request Aug 17, 2026
Treat CH_SIG as channel-owned state after the kernel dequeues a
caught signal. A blocked host retry now completes with EINTR before it
can re-park, so libc can run the handler without losing the only signal
record.

Forward-port #1129 onto ABI 43 exact retry ownership. Host-generated
events now enter through an additive kernel export instead of borrowing
the target task's occupied syscall channel. Preserve public nonblocking
EAGAIN, SA_RESTART policy, and Node/browser parity.

Validation:
- 1,500 native kernel tests
- 177 focused blocking-retry and signal tests
- real accept/SIGCHLD guest in Node.js
- real accept/SIGCHLD guest in Chromium, Firefox, and WebKit
- ABI snapshot, host typecheck, and documentation build

(cherry picked from commit 997fc7b)
brandonpayton added a commit that referenced this pull request Aug 17, 2026
Treat CH_SIG as channel-owned state after the kernel dequeues a
caught signal. A blocked host retry now completes with EINTR before it
can re-park, so libc can run the handler without losing the only signal
record.

Forward-port #1129 onto ABI 43 exact retry ownership. Host-generated
events now enter through an additive kernel export instead of borrowing
the target task's occupied syscall channel. Preserve public nonblocking
EAGAIN, SA_RESTART policy, and Node/browser parity.

Validation:
- 1,500 native kernel tests
- 177 focused blocking-retry and signal tests
- real accept/SIGCHLD guest in Node.js
- real accept/SIGCHLD guest in Chromium, Firefox, and WebKit
- ABI snapshot, host typecheck, and documentation build

(cherry picked from commit 997fc7b)
(cherry picked from commit 775903e)
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.

1 participant