Skip to content

WinIO: skip the completion port when a socket request finishes at once - #639

Closed
thomasjm wants to merge 1 commit into
haskell:masterfrom
codedownio:winio-skip-completion-on-success
Closed

thomasjm wants to merge 1 commit into
haskell:masterfrom
codedownio:winio-skip-completion-on-success

Conversation

@thomasjm

Copy link
Copy Markdown
Contributor

This is the fix for #638, as described in the issue and in the repro.

@kazu-yamamoto

kazu-yamamoto commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

Thank you for the careful analysis and the repro. I checked it locally on Windows 11 with GHC 9.12.4.20260713 (__IO_MANAGER_WINIO__ = 2), building crash against network master (9ce1211) and against master with this PR cherry-picked:

  • sync_completion.c: without the flag, 100/100 WSARecv and WSASend that finish at once each queue a packet; with it, none do.
  • crash: master crashed within a second, three runs out of three (Access violation in generated code when reading 0x101010101010109, exit code 11); with this PR it survived 120s.
  • The test suite passes with this PR under WinIO and MIO, and with GHC 9.8.2.

I also confirmed in GHC's withOverlappedEx that a CbPending start calls getOverlappedResult without waiting and completes the request inline when it has finished, so the packet left on the port is the only remaining reference.

Note that CI does not exercise this: its Windows jobs use GHC 9.4 and 9.14.1, which compile network's WinIO code out.

A few points:

  1. When SetFileCompletionNotificationModes fails, associateSocket raises a bare userError. Could it raise the GetLastError code instead, like other Winsock errors (throwSocketErrorCode)? The socket is closed by bracketOnError either way.
  2. The non-IFS LSP check is not needed, I think: LSPs have been deprecated since Windows 8.
  3. This now conflicts with Give Winsock errors the IOErrorType of their errno #637 in Network/Socket/Cbits.hsc (both add definitions after wsaENOPROTOOPT); keeping both sides is all it takes. Could you rebase onto master?

@Mistuke In #602 you mentioned that FILE_SKIP_COMPLETION_PORT_ON_SUCCESS is not valid for sockets. Microsoft documents it for sockets except behind a non-IFS LSP, and libuv sets it on its TCP sockets in the same way (uv__tcp_set_socket). Are you OK with this change?

kazu-yamamoto added a commit that referenced this pull request Nov 14, 2026
@kazu-yamamoto

Copy link
Copy Markdown
Collaborator

Merged into master as 0d8b844 after rebasing onto it locally (the conflict with #637 was only adjacent definitions in Cbits.hsc; your commit is kept as 5421eed). Thank you!

kazu-yamamoto added a commit that referenced this pull request Nov 14, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kazu-yamamoto added a commit that referenced this pull request Nov 14, 2026
waitAndCancelReadSocketSTM and waitAndCancelWriteSocketSTM issue an
AFD poll on the socket, which completes on the I/O manager's port like
any overlapped operation; cancelling kills the waiting thread.  MIO
still throws.  Relies on FILE_SKIP_COMPLETION_PORT_ON_SUCCESS (#639).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants