Skip to content

Fix a macOS call to a full cell reporting unavailable instead of capacity - #111

Merged
flavorjones merged 1 commit into
masterfrom
card-658-macos-sendmsg-enotconn
Oct 10, 2026
Merged

flavorjones merged 1 commit into
masterfrom
card-658-macos-sendmsg-enotconn

Conversation

@flavorjones

Copy link
Copy Markdown
Member

Motivation

A full cell writes capacity and closes the connection without reading the request (Supervisor#refuse). The client's send then fails. Since #67, Transport::Socket#deliver ignores that failure when it is Errno::EPIPE or Errno::ECONNRESET, so receive reads the capacity answer.

On macOS the send sometimes fails with Errno::ENOTCONN instead. deliver doesn't rescue it, so the call reports unavailable. test_an_answer_that_arrives_before_the_request_is_sent_is_still_the_verdict failed on the macOS job for this reason (run):

unavailable: Errno::ENOTCONN: Socket is not connected - sendmsg(2).
Expected: "capacity"
  Actual: "unavailable"

The XNU kernel's sosend checks the socket under its lock. It then drops the lock to copy the request in, and calls uipc_send without checking again. If the cell's close lands in that window, uipc_send tests SS_ISCONNECTED before SS_CANTSENDMORE and returns ENOTCONN instead of EPIPE. In a probe on a macOS runner (run), sendmsg raised ENOTCONN in 5 of 5,000 tries, and the answer was readable after every one. On Linux it never did.

Clients on Linux are not affected.

Details

deliver also rescues Errno::ENOTCONN.

On Linux, ENOTCONN from the send means the socket never connected because the cell's backlog was full (see the comment on call). That call still reports unavailable at once, because the read that follows fails with Errno::EINVAL. Its detail changes from Errno::ENOTCONN: Transport endpoint is not connected - sendmsg(2) to Errno::EINVAL: Invalid argument. ControlTimeoutTest covers that path.

The new test stubs Connection#send_message to raise Errno::ENOTCONN once the cell's answer is on the socket. Before this change it failed with the same message as the macOS job.

Fixes #110

Copilot AI balanced review requested due to automatic review settings October 10, 2026 02:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

On macOS, a call to a full cell occasionally reported `unavailable`
instead of `capacity`. A full cell answers and closes without reading
the request, so the client's send fails. macOS sometimes reported that
failure as `ENOTCONN`, but `deliver` ignored only `EPIPE` and
`ECONNRESET`.

Ignore `ENOTCONN` too. The comment on `deliver` names the kernel race,
and the comment on `call` says how a full backlog on Linux still
reaches `unavailable`.

[Fix #110]
@flavorjones
flavorjones force-pushed the card-658-macos-sendmsg-enotconn branch from 3990ec8 to cb8df4a Compare October 10, 2026 17:06
@flavorjones
flavorjones merged commit b78f917 into master Oct 10, 2026
33 checks passed
@flavorjones
flavorjones deleted the card-658-macos-sendmsg-enotconn branch October 10, 2026 17:10
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.

Test flake on macOS: sendmsg raises ENOTCONN, so an early capacity answer reads as unavailable

2 participants