client: happy eyeballs: enable POLLOUT on the racing fd, not the primary - #3648
Closed
saghul wants to merge 1 commit into
Closed
client: happy eyeballs: enable POLLOUT on the racing fd, not the primary#3648saghul wants to merge 1 commit into
saghul wants to merge 1 commit into
Conversation
On POSIX, lws_client_connect_3_connect() restores the parallel swap (wsi->desc and wsi->position_in_fds_table back to the primary socket) before doing the "hear about the connect completion as a POLLOUT event" lws_change_pollfd(), so for a racing (parallel) connect the POLLOUT is applied to the primary socket instead of the fd that connect() was just called on. A racer therefore only ever has the POLLIN that __insert_wsi_socket_into_fds() seeded: it can be seen to have failed (POLLERR/POLLHUP surface on a POLLIN watch) but never to have succeeded, so happy eyeballs can never promote a winner, and if the primary attempt fails after racers consumed the remaining DNS results the connection dies with no fallback. The quic-race branch in the same function clears POLLOUT on a connected racer, so racers are clearly expected to have it. Move the POLLOUT enable above the swap restore, so it lands on the socket the connect() was issued on in both the primary and the racing case.
|
Contributor
Author
This was referenced Jul 29, 2026
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.



Found while implementing the new parallel-connect event lib ops (
sock_accept_parallel/io_parallel/close_handle_manually_parallel/promote_parallel, added ind31f2d830"event-loop: HE races") in a custom event lib that drives lws off a libuv loop.On POSIX,
lws_client_connect_3_connect()restores the parallel swap —wsi->descandwsi->position_in_fds_tableback to the primary socket — before it does the "hear about the connect completion as a POLLOUT event"lws_change_pollfd():So for a racing connect the POLLOUT is applied to the primary socket rather than to the fd
connect()was just called on. A racer ends up with only the POLLIN that__insert_wsi_socket_into_fds()seeded, which means it can be seen to have failed (POLLERR/POLLHUP surface on a POLLIN watch) but never to have succeeded:try_next_dns_resulthas nothing left and the connection dies with no fallback — which regresses the classic broken-IPv6 case.That racers are expected to carry POLLOUT is visible in the same function: the quic-race branch explicitly clears it on a connected racer via the swap dance (
lws_change_pollfd(wsi, LWS_POLLOUT, 0)).The fix moves the POLLOUT enable above the swap restore, so it lands on the socket the
connect()was issued on in both the primary and the racing case. Thegoto try_next_dns_result_fdserror path already expects the swap to still be active (it restores it itself), so jumping to it from there is unchanged.Tested on macOS with a libuv-based event lib implementing the parallel ops, with the happy-eyeballs delay temporarily forced to 1us so that every multi-address connect races. Before: https fetches fail with
Timed out waiting SSL, or hang on a racer that can never be promoted. With this plus the companion promote fix: all complete normally, the process exits cleanly, full test suite (329 tests) green, ASAN clean.