Deliver a receive through STM, and stop pretending readiness works on Windows - #627
Open
kazu-yamamoto wants to merge 1 commit into
Open
kazu-yamamoto wants to merge 1 commit into
kazu-yamamoto wants to merge 1 commit into
Conversation
… Windows waitReadSocketSTM is threadWaitReadSTM on the raw socket. GHC compiles the event manager branch of threadWaitRead out on Windows, so it falls back to the waitRead# primop, which the threaded RTS does not implement for sockets. The STM action therefore never fires -- under the old I/O manager as much as under WinIO, so this is not a WinIO regression. It blocks forever and says nothing. Readiness cannot be recovered on Windows in general: completion ports report that an operation finished, not that one could be started. So offer the completion shape instead. recvBufSTM and recvBufFromSTM, and recvSTM and recvFromSTM for ByteStrings, start a receive and hand its result over through STM. They work on every platform, and on Windows they are what callers who used waitReadSocketSTM want. The four readiness functions are now POSIX only and throw on Windows, naming what to do instead: use one of the receives above, or, for the write side, just issue the send -- completion ports give no way to ask whether one would block and none is needed. Cancelling a receive can lose data that the kernel has already dequeued, and needs the receive to be interruptible, which the old Windows I/O manager does not provide. Both are documented, and the cancellation test is POSIX only for the second reason. Co-Authored-By: Claude Opus 5 (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.
The problem
waitReadSocketSTMisthreadWaitReadSTMon the raw socket. GHC compiles theevent manager branch out on Windows:
so it always falls back to the
waitRead#primop, which the threaded RTS doesnot implement for sockets. The STM action never fires.
Measured on Windows 11 with GHC 9.12.5-rc3, binding a UDP socket, asking for the
STM action and then sending a datagram to it:
The control
recvFrompicks up the very datagram the STM was waiting for, so thesocket was readable the whole time. This is not a WinIO regression: it is
equally broken under the old I/O manager. It blocks forever and says nothing.
This is what
quichit. Its server dispatcher waits onwaitReadSocketSTMbefore every receive, so on Windows it never reaches
recvFromat all and everyconnection times out during the handshake.
Why readiness cannot simply be fixed
Completion ports report that an operation finished, not that one could be
started. There is no readiness to wait for.
A zero-byte overlapped
WSARecvis the usual way to fake it, and it does detectarrival — but on a datagram socket it discards the datagram (truncated and
dropped). Verified:
MSG_PEEKMSG_PEEKSo it is possible, with a 1-byte
MSG_PEEKreceive. But it costs an extrasyscall per receive, it has no counterpart for writes, and it is an emulation of
the wrong shape. This PR does not do that.
What this PR does instead
Offer the completion shape, which is what the platform actually provides:
These start a receive and hand its result over through STM. No platform
#if:on Windows under WinIO the receive goes through
withOverlapped, sokillThreadtriggers
CancelIoEx; on POSIX it interruptsthreadWaitRead.On Windows the four readiness functions now fail loudly instead of hanging, and
name what to do instead:
waitReadSocketSTM/waitAndCancelReadSocketSTMpoint at the receives above.waitWriteSocketSTM/waitAndCancelWriteSocketSTMthrow as well. Completionports give no way to ask whether a send would block, and none is needed: issue
the send.
Two caveats are in the Haddock:
should not be abandoned casually when composed with
orElse.WinIO, but not under the old Windows I/O manager, where the receive blocks in a
foreign call that
killThreadcannot reach. The cancellation test is POSIXonly for that reason.
Testing
--io-manager=nativeand five under--io-manager=posix. (82 on master, plustwo of the three new tests; the cancellation one is POSIX only.)
Note that CI does not exercise WinIO.
configure.acmatchescase "$host_os" in mingw*), but cabal runsconfigureunder an MSYS shell, soautoconf resolves the host to
x86_64-pc-msysand--io-manager=nativeis neveradded. That is a separate problem.
Refs #602.
🤖 Generated with Claude Code