Skip to content

Fix win32 select handle leak - #14975

Open
toots wants to merge 2 commits into
ocaml:trunkfrom
toots:fix-win32-select-handle-leak
Open

Fix win32 select handle leak#14975
toots wants to merge 2 commits into
ocaml:trunkfrom
toots:fix-win32-select-handle-leak

Conversation

@toots

@toots toots commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

This is a LLM-driven bug reported initially by liquidsoap users at savonet/liquidsoap#5290 and confirmed on the code base.

The commits are split in two:

  1. First commit adding a regression test and showing a socket not being properly cleaned up. This was tested with wine locally which does not implement GetProcessHandleCount so the test was switched to trigger on the socket not being restored to non-blocking, which is one of the process applied when the socket is properly handled when exiting the select loop. A more rigorous test demonstrating the socket leak can also be implemented but I will not be able to run it locally.
  2. Second commit introduces the fix and flips the test reference back to OK.

PS: this message is written by a reguler person (myself!)

When the descriptor set is not made exclusively of sockets, Unix.select
falls back to a worker thread emulation which registers every socket
with WSAEventSelect for the duration of the call. If an error occurs,
the sockets are currently left registered and in non-blocking mode, and
one event handle per socket is leaked.

The test detects the skipped cleanup through Unix.clear_nonblock:
ioctlsocket(FIONBIO) fails with WSAEINVAL while a WSAEventSelect
registration is active. Its reference file records the current, buggy
behaviour, so that the next commit shows it flipping to a successful
cleanup.
@toots
toots force-pushed the fix-win32-select-handle-leak branch from b041f75 to 1092bd9 Compare July 31, 2026 00:49
@toots toots changed the title [WIP] Fix win32 select handle leak Fix win32 select handle leak Jul 31, 2026
In socket_poll, the cleanup loop releasing the event handles, cancelling
the WSAEventSelect associations and restoring the blocking mode of the
sockets was guarded by nError == 0, so any error during setup or during
the wait skipped it altogether. This leaked one event handle per socket
and left the sockets registered with a destroyed event and in
non-blocking mode.

Cleanup is now unconditional; only the collection of the results
remains conditioned on the absence of error. A CreateEvent failure is
also reported instead of being silently turned into a WAIT_FAILED.

The event array was additionally one element too small: it holds one
event per query plus hStop, and the number of queries per worker is
allowed to reach MAXIMUM_SELECT_OBJECTS.

The reference of the test added in the previous commit flips from the
skipped cleanup to a successful one.
@toots
toots force-pushed the fix-win32-select-handle-leak branch from 1092bd9 to c8da08e Compare July 31, 2026 01:11
@nojb

nojb commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
  1. This was tested with wine locally which does not implement GetProcessHandleCount so the test was switched to trigger on the socket not being restored to non-blocking, which is one of the process applied when the socket is properly handled when exiting the select loop. A more rigorous test demonstrating the socket leak can also be implemented but I will not be able to run it locally.

For a more direct examination of the bug you could try using the debug runtime (-runtime-variant d) and setting OCAMLDEBUG=1. This will enable the various DEBUG_PRINT statements in the code.

@toots

toots commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author
  1. This was tested with wine locally which does not implement GetProcessHandleCount so the test was switched to trigger on the socket not being restored to non-blocking, which is one of the process applied when the socket is properly handled when exiting the select loop. A more rigorous test demonstrating the socket leak can also be implemented but I will not be able to run it locally.

For a more direct examination of the bug you could try using the debug runtime (-runtime-variant d) and setting OCAMLDEBUG=1. This will enable the various DEBUG_PRINT statements in the code.

How do you think this could be used?

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.

3 participants