Report EINPROGRESS/EALREADY for in-flight non-blocking TCP connect#856
Open
brandonpayton wants to merge 1 commit into
Open
Report EINPROGRESS/EALREADY for in-flight non-blocking TCP connect#856brandonpayton wants to merge 1 commit into
brandonpayton wants to merge 1 commit into
Conversation
POSIX/Linux require a non-blocking `connect()` that cannot complete synchronously to fail with `EINPROGRESS` on the first call and `EALREADY` while the handshake is still pending. Kandelo returned `EAGAIN` instead, so the standard async-connect idiom — `O_NONBLOCK` → `connect()` → `poll(POLLOUT)` → `getsockopt(SO_ERROR)` — treated the connection as a hard failure. This affects external and virtual-network TCP clients (curl, redis-cli, event-loop libraries); loopback demos were unaffected because loopback connect completes synchronously via `cross_process_loopback_connect`. Kernel (`sys_connect`): the external two-phase path now maps the in-flight status to `EINPROGRESS` on the first call (transition into `Connecting`) and `EALREADY` on subsequent calls, instead of `EAGAIN`. The kernel owns connect state, so it is the correct source of these errnos. Host (`kernel-worker.ts`): the syscall-completion dispatch routes a connect that returns `EINPROGRESS`/`EALREADY` — for a non-blocking fd it returns the errno to the guest as-is; for a blocking fd it feeds the same retry loop previously keyed on `EAGAIN`, so blocking connect still waits for the handshake. No ABI change: no new exports/imports or repr(C) types; snapshot in sync. Validation: `cargo test -p kandelo --lib` (964 pass, incl. new `test_nonblocking_connect_reports_einprogress_then_ealready`, which drives `sys_connect` against a host reporting an in-flight handshake and asserts EINPROGRESS then EALREADY); host `npm run typecheck` clean; `scripts/check-abi-version.sh` reports snapshot in sync. End-to-end guest behavior follows from the kernel errno + the host routing (no prebuilt kernel wasm is available in this environment to drive a full two-process integration run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Contract: non-blocking
connect()must report EINPROGRESS, not EAGAINPOSIX/Linux require a non-blocking
connect()that can't complete synchronously to fail withEINPROGRESSon the first call andEALREADYwhile the handshake is pending. Kandelo returnedEAGAIN, so the standard async-connect idiom (O_NONBLOCK→connect→poll(POLLOUT)→getsockopt(SO_ERROR)) treated the connection as a hard failure.Affects external and virtual-network TCP clients (curl, redis-cli, event-loop libraries). Loopback demos (nginx→PHP-FPM, WordPress→MariaDB) were unaffected because loopback connect completes synchronously via
cross_process_loopback_connect, which is why it went unnoticed.Fix
sys_connect): the external two-phase path maps the in-flight status toEINPROGRESSon the first call (transition intoConnecting) andEALREADYon subsequent calls, instead ofEAGAIN. The kernel owns connect state, so it is the correct source of these errnos.kernel-worker.ts): the completion dispatch routes a connect returningEINPROGRESS/EALREADY— non-blocking fd → return the errno to the guest as-is; blocking fd → feed the same retry loop previously keyed onEAGAIN, so blocking connect still waits for the handshake.ABI
No change — no new exports/imports or
repr(C)types.scripts/check-abi-version.sh: snapshot in sync.Validation
cargo test -p kandelo --target aarch64-apple-darwin --lib— 964 pass, including newtest_nonblocking_connect_reports_einprogress_then_ealready, which drivessys_connectagainst aMockHostIOreporting an in-flight handshake and asserts EINPROGRESS then EALREADY (it returned EAGAIN twice before the fix).npm run typecheck— clean.scripts/check-abi-version.sh— snapshot in sync.Scope note: end-to-end guest behavior follows from the kernel errno + the host routing; no prebuilt kernel wasm is available in this environment to drive a full two-process integration run, so that link is validated by construction rather than an integration test.
🤖 Generated with Claude Code