Bound a channel's sends: macOS ignores MSG_DONTWAIT on a blocking unix socket - #302
Closed
scgopi wants to merge 2 commits into
Closed
Bound a channel's sends: macOS ignores MSG_DONTWAIT on a blocking unix socket#302scgopi wants to merge 2 commits into
scgopi wants to merge 2 commits into
Conversation
…x socket OutboundChannel's writer claimed to be non-blocking per call and to wait for writability in slices, which is what let closeAndWait promise to return on a peer that stopped reading. On macOS the claim was false: send(2) with MSG_DONTWAIT on a blocking AF_UNIX stream socket blocks anyway, so the writer parked inside the syscall and never reached the loop — teardown of a wedged client was unbounded. SO_SNDTIMEO, one slice long, set on the socket at open, makes the call return what it wrote (or EAGAIN) after the slice, which is the shape the loop was written for; the reader sharing the descriptor keeps its blocking reads. The comments now describe the mechanism that works. The actor was never exposed — the per-connection thread is the fix that mattered — this is teardown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DeGL2CxuGmq16RSZpJYm2N
Owner
Author
|
Duplicate of #301, which landed the same |
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.
Split out of the #289 diagnostics PR at PerfTriage's request: it fixes shipped behaviour (#291 is in 0.1.64-beta1), it is two lines with its own test, and #289's
blocked_msaccounting depends on it.The defect
OutboundChannel.writeFrameclaimed to be non-blocking per call (MSG_DONTWAIT) and to wait for writability in boundedpollslices — which is what letcloseAndWaitpromise to return on a peer that stopped reading. On macOS the claim was false:send(2)withMSG_DONTWAITon a blocking AF_UNIX stream socket blocks anyway; the flag is ignored. Reproduced three ways — Python socketpair with 4 KB buffers (60 KBsend: still inside the syscall after a second; PerfTriage saw 120 s), and through the channel itself (60 KB queued, 4096 readable at the peer, no line from the loop). So the writer parked inside the syscall and never reached its loop.What that did and did not break: the actor was never exposed — the per-connection thread is the fix that mattered, and #288's stall is genuinely gone. What was wrong is teardown:
closeAndWaitaftershutdownstill returns (ashutdowndoes wake a parkedsendhere), butdetach— the path for a descriptor number a new connection has already taken over — cannot shut the socket down, and a writer parked insendnever noticesisClosing; it waits for the dead peer's lifetime. And nothing in the loop could be measured.The change
SO_SNDTIMEO, one poll slice (50 ms), set on the channel's socket at open (boundSends). A blocking send then returns what it wrote, orEAGAINwhen nothing went, after the slice — exactly the shape the loop was written for — andpoll(POLLOUT)reports honestly afterwards (verified: 60 KB into a 4 KB peer returns 4096 after 0.052 s; the following poll says not-ready). It bounds sends only: the reader sharing the descriptor keeps its blocking reads, whichO_NONBLOCKcould not promise since it lives on the open file description.MSG_DONTWAITstays for Linux, where it is honoured. The comments onwriteFramenow describe the mechanism that works rather than the one that did not.Verification
OutboundChannelBoundedSendTests: a detached writer wedged on a deaf 4 KB peer retires within a bounded time (fails without the line — the writer never returns fromsendand the test process cannot exit; passes with it), and a slow reader still receives a whole 20 KB frame. ExistingOutboundChannelTestsandOutboundChannelReviewTestspass unchanged.Gate: full Xcode suite (gate on this head in flight; reported in a comment when it completes), swiftlint 0 errors, swift-format clean,
graphcodedandgraphcode-clischemes build; SwiftPMswift buildpasses (the Linux branch keepsMSG_DONTWAIT).Refs #288, #291, #289.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DeGL2CxuGmq16RSZpJYm2N