Merge nucleic/hazy-opal-yak-cjc0 into dev

This commit is contained in:
2026-07-17 23:48:59 -07:00
parent 608e7d0450
commit 65623f1c34
5 changed files with 403 additions and 202 deletions
+34 -2
View File
@@ -205,6 +205,36 @@ rebuild whenever a guest patch changes. Built locally, not in CI: the host frame
into a never-accepted backlog. A failed pre-relay connection is also closed explicitly.
Marked `[Nucleic vendored patch]`.
14. **Non-blocking, non-spinning guest I/O plane (`IOPair.swift`, `OSFile+Splice.swift`,
`VsockProxy.swift`) — the root cause of the "control plane unresponsive / all sessions stall"
wedge.** Every exec's stdio relay (`IOPair`) and every control-plane relay connection
(`VsockProxy`) share ONE thread: `ProcessSupervisor.default`'s epoll poller. Three defects let
a single slow peer freeze that thread — and with it every session's stdio AND the whole
container's control plane at once (probes then read "accepts connections but never answers";
before the manager-side recovery rework the host restarted the container over this, SIGKILLing
every session):
- **`IOPair` blocking write:** only *registered* fds get `O_NONBLOCK` (set by `Epoll.add`), and
the relay registers only its READ fd — the write fd (e.g. the vsock socket carrying an exec's
stdout to the host) stayed blocking. One slow-drained stream parked the poller thread in
`write(2)`. The relay now sets the write fd non-blocking up front and implements real
backpressure: a full destination stashes the remainder in a `pending` backlog, registers the
write fd for EPOLLOUT, and suspends reads until the flush completes (throttling the producing
process via its own pipe, not the shared thread). The old close-on-short-write path — dead
while the fd was blocking, live and stdio-dropping once non-blocking — is subsumed by the
backlog; genuine write errors still close the pair.
- **`OSFile.splice` busy-spin:** when the destination was full (EAGAIN) and the source idle,
the outer loop had no exit — it spun the poller thread at 100% until the peer drained (or
forever if it never did). The flush leg's EAGAIN branch now returns partial progress; the
un-flushed bytes stay in the transfer pipe and the destination's EPOLLOUT edge resumes the
flush (both `VsockProxy` handlers already pump both directions on both events).
- **`VsockProxy` registration race:** the client fd's epoll handler can fire — and splice
toward the server fd — before the second registration makes that fd non-blocking; a full
destination made that a genuinely blocking splice on the poller thread. Both fds are now set
non-blocking before either is registered.
All three are guest-side and INERT until the initfs image is rebuilt (`make vminit-image`, tag
`0.34.0-nucleic4`) and `ContainerEngine.vminitReference` is bumped after runtime validation.
Marked `[Nucleic vendored patch]`.
## Re-vendoring a newer upstream commit
1. `git clone` upstream (or copy `.build/checkouts/containerization` after bumping the URL pin
@@ -222,9 +252,11 @@ rebuild whenever a guest patch changes. Built locally, not in CI: the host frame
`Cgroup2Manager.swift` / `ManagedContainer.swift` / `ManagedProcess.swift`, patch #10
(`Socket.acceptStream` transient-error tolerance + `isTransientAcceptError`), patch #11 (the
`UnixSocketRelay` per-connection containment + fail-fast closes), patch #12 (the `VsockProxy`
cleanup/`try!`/listener hardening in `vminitd/`), and patch #13 (the prompt-free
cleanup/`try!`/listener hardening in `vminitd/`), patch #13 (the prompt-free
`KeychainQuery` reads: `withoutInteractiveUI` + the `errSecInteractionNotAllowed` handling +
the `save` duplicate retry). After re-applying any
the `save` duplicate retry), and patch #14 (the non-blocking/non-spinning guest I/O plane:
`IOPair` backpressure, the `OSFile.splice` EAGAIN return, and the `VsockProxy` pre-registration
non-blocking fds — all in `vminitd/`). After re-applying any
`vminitd/` patch, rebuild + publish the custom init image
with `make vminit-image` + `make vminit-image-push`, and bump `ContainerEngine.vminitReference`.
5. Update the commit hash above and in the root `Package.swift` comment.