fix(host): fix Windows networking read-readiness detection - #105
Closed
danbugs wants to merge 5 commits into
Closed
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Windows socket readiness polling in the host to avoid WSAPoll reliability issues and to ensure the guest scheduler can wake on both receive and send readiness, then bumps the crate version for release.
Changes:
- Windows: replace
WSAPollwith Winsockselect()inhandle_net_pollandhl_sleep_poll_sockets. - Unix: adjust
hl_sleep_poll_socketspolling/readiness logic to consider write readiness (and intended to wake on send as well as receive). - Bump host crate version to
0.11.1.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
host/src/lib.rs |
Reworks Windows polling to use select() and changes sleep polling readiness detection. |
host/Cargo.toml |
Bumps crate version for the planned crates.io release. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
There was a problem hiding this comment.
Linux Benchmarks
Details
| Benchmark suite | Current: ecfe2df | Previous: 682ad20 | Ratio |
|---|---|---|---|
hello_world (median) |
20 ms |
20 ms |
1 |
pandas (median) |
110 ms |
120 ms |
0.92 |
density (per VM) |
8 MB |
8 MB |
1 |
snapshot (disk) |
653 MiB |
653 MiB |
1 |
This comment was automatically generated by workflow using github-action-benchmark.
Contributor
There was a problem hiding this comment.
Windows Benchmarks
Details
| Benchmark suite | Current: ecfe2df | Previous: 682ad20 | Ratio |
|---|---|---|---|
hello_world (median) |
277 ms |
291 ms |
0.95 |
pandas (median) |
870 ms |
1024 ms |
0.85 |
density (per VM) |
7 MB |
7 MB |
1 |
snapshot (disk) |
661 MiB |
661 MiB |
1 |
This comment was automatically generated by workflow using github-action-benchmark.
danbugs
force-pushed
the
fix/wsapoll-pollout-spin
branch
2 times, most recently
from
June 30, 2026 06:08
c3d7e73 to
9960572
Compare
On Windows, the Unikraft guest busy-polls net_poll(timeout_ms=0) without calling __hl_sleep, and also relies on __hl_sleep socket_ready to detect POLLOUT before sending data. Two fixes: 1. handle_net_poll: enforce a 50ms minimum select() timeout when readfds is populated, giving select() time to detect incoming data between the guest's zero-timeout poll cycles. 2. hl_sleep_poll_sockets: check writefds in addition to readfds so POLLOUT (write-readiness) is reported back to the guest. Without this, urllib.request.urlopen with timeout= hangs because the guest never learns the socket is writable. Also restores SO_RCVTIMEO/SO_SNDTIMEO after connect_timeout(), which internally resets the socket to blocking mode and may clear timeouts. Removes the ioctlsocket(FIONREAD) fallback that was added as a workaround — select() with a proper timeout handles this correctly. Replaces WSAPoll with select() on Windows throughout, as WSAPoll has known issues with POLLOUT spin on connected TCP sockets. Signed-off-by: danbugs <danilochiarlone@gmail.com>
Adds urllib_get_no_timeout.py which exercises the code path used by mxc — calling urlopen() without an explicit timeout. This path relies on the host's net_poll select() to detect read-readiness, unlike the timeout= path which uses Python's internal select loop. Adds matrix entries for both Linux and Windows CI. Signed-off-by: danbugs <danilochiarlone@gmail.com>
Signed-off-by: danbugs <danilochiarlone@gmail.com>
Signed-off-by: danbugs <danilochiarlone@gmail.com>
danbugs
force-pushed
the
fix/wsapoll-pollout-spin
branch
from
June 30, 2026 08:17
b6e08b7 to
ab58739
Compare
The guest busy-poll with timeout_ms=0 issue also affects Linux. Apply the same 50ms minimum timeout when POLLIN is requested, matching the Windows fix. Signed-off-by: danbugs <danilochiarlone@gmail.com>
2 tasks
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.
Summary
Fix outbound HTTP from micro-VMs on Windows. Two issues caused
urllib.request.urlopen()to hang:Guest busy-polls
net_poll(timeout_ms=0)without yielding — The Unikraft guest kernel callsnet_pollwith a zero timeout in a tight loop. The host'sselect()returns immediately without detecting incoming data. Fix: enforce a 50ms minimumselect()timeout when read-readiness is being checked, giving the OS time to detect TCP response data.hl_sleep_poll_socketsonly checked readfds — When Python usessettimeout(), the guest waits for POLLOUT (write-readiness) via__hl_sleepbefore sending the HTTP request. Buthl_sleep_poll_socketsonly checkedreadfds, so POLLOUT was never reported and the guest hung before even sending. Fix: checkwritefdsalongsidereadfdsandexceptfds.Also:
SO_RCVTIMEO/SO_SNDTIMEOafterconnect_timeout()which resets socket stateWSAPollwithselect()throughout (WSAPoll has known POLLRDNORM issues)ioctlsocket(FIONREAD)fallback (no longer needed with proper select timeouts)urlopen()without timeout (the mxc code path)Test plan
urlopen("http://example.com/")without timeout — passes 3/3 on Windowsurlopen("http://example.com/", timeout=10)— passes 3/3 on Windowsurllib_get_no_timeout.py(Linux + Windows matrix entries)