Skip to content

WinIO the POSIX way: non-blocking sockets, readiness-based give-ups - #643

Merged
kazu-yamamoto merged 5 commits into
haskell:masterfrom
kazu-yamamoto:winio-posix
Nov 15, 2026
Merged

kazu-yamamoto merged 5 commits into
haskell:masterfrom
kazu-yamamoto:winio-posix

Conversation

@kazu-yamamoto

@kazu-yamamoto kazu-yamamoto commented Nov 15, 2026 •

Copy link
Copy Markdown
Collaborator

The next item of #642: under WinIO, sockets behave as on POSIX, waiting for readiness through the AFD poll of #641.

  • Non-blocking WinIO sockets. socket and accept put them in non-blocking mode (FIONBIO), as on POSIX. Overlapped operations are not affected by the mode; a plain call which would block says WSAEWOULDBLOCK, and the throwSocketErrorWait* guards wait for readiness and retry, as they do with threadWaitRead on POSIX.
  • accept and connect take the POSIX path. No thread blocks in a foreign call any more; connect reads SO_ERROR once the socket is writable.
  • The give-up API waits for readiness and runs the operation once the socket is ready, through the same withReady as POSIX, instead of starting it in a thread of its own and cancelling it with CancelIoEx. Giving up ends the wait alone, so nothing received is lost and other threads' operations on the socket are left alone. A socket which is ready already is found with WSAPoll and a zero timeout, as poll is used on POSIX. connectWith connects without blocking.
  • WinIO failures go through throwSocketErrorCode, so they carry the Winsock code in ioe_errno with the IOErrorType it stands for (Give Winsock errors the IOErrorType of their errno #632), instead of the type GHC's withException gives. A stream reset (WSAECONNRESET) was taken for the end of the stream; it is raised, as on POSIX and with No IO Manager.
  • Network.Socket.Win32.AfdPoll holds the poll, so that Network.Socket.Internal can use it too.

The No IO Manager code is unchanged; removing it is the next item.

Tested on Windows 11 with GHC 9.12.4.20260713: 123 examples, 0 failures under WinIO and with No IO Manager (7 pending), and with GHC 9.8.2. macOS: 127 examples, 0 failures. Three tests, "an ICMP unreachable from a send of one's own", come and go on that machine: master and this branch both failed them 10 times out of 10 in one series and passed them in another, so they depend on the state of Windows (it seems to stop sending ICMP errors for a while after heavy traffic), not on this change. CI's Windows jobs compile WinIO out, so this is tested locally only.

🤖 Generated with Claude Code

kazu-yamamoto and others added 5 commits November 15, 2026 09:42
So that Network.Socket.Internal can wait through it too, and add a
blocking afdWait beside the STM one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As on POSIX, a call which would block says WSAEWOULDBLOCK, and the
throwSocketErrorWait* guards wait for readiness and retry.  accept and
connect take the POSIX path under WinIO: no thread blocks in a foreign
call, and connect reads SO_ERROR once the socket is writable.
Overlapped operations are not affected by the mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GHC's withException raised them with its own error type; going through
throwSocketErrorCode keeps the Winsock code in ioe_errno with the
IOErrorType it stands for.  A stream reset was taken for the end of the
stream; it is raised, as on POSIX and under MIO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under WinIO the give-up API waits for readiness through the AFD poll and
runs the operation once the socket is ready, as on POSIX, rather than
starting it in a thread of its own and cancelling it.  Giving up ends
the wait alone: nothing received is lost, and other threads' operations
on the socket are left alone.  connectWith connects without blocking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kazu-yamamoto
kazu-yamamoto merged commit 1f3c55a into haskell:master Nov 15, 2026
11 checks passed
kazu-yamamoto added a commit that referenced this pull request Nov 15, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kazu-yamamoto
kazu-yamamoto deleted the winio-posix branch November 15, 2026 01:20
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.

1 participant