Skip to content

fix(host): fix Windows networking read-readiness detection - #105

Closed
danbugs wants to merge 5 commits into
mainfrom
fix/wsapoll-pollout-spin
Closed

fix(host): fix Windows networking read-readiness detection#105
danbugs wants to merge 5 commits into
mainfrom
fix/wsapoll-pollout-spin

Conversation

@danbugs

@danbugs danbugs commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix outbound HTTP from micro-VMs on Windows. Two issues caused urllib.request.urlopen() to hang:

  1. Guest busy-polls net_poll(timeout_ms=0) without yielding — The Unikraft guest kernel calls net_poll with a zero timeout in a tight loop. The host's select() returns immediately without detecting incoming data. Fix: enforce a 50ms minimum select() timeout when read-readiness is being checked, giving the OS time to detect TCP response data.

  2. hl_sleep_poll_sockets only checked readfds — When Python uses settimeout(), the guest waits for POLLOUT (write-readiness) via __hl_sleep before sending the HTTP request. But hl_sleep_poll_sockets only checked readfds, so POLLOUT was never reported and the guest hung before even sending. Fix: check writefds alongside readfds and exceptfds.

Also:

  • Restore SO_RCVTIMEO/SO_SNDTIMEO after connect_timeout() which resets socket state
  • Replace WSAPoll with select() throughout (WSAPoll has known POLLRDNORM issues)
  • Remove the ioctlsocket(FIONREAD) fallback (no longer needed with proper select timeouts)
  • Add CI test for urlopen() without timeout (the mxc code path)

Test plan

  • urlopen("http://example.com/") without timeout — passes 3/3 on Windows
  • urlopen("http://example.com/", timeout=10) — passes 3/3 on Windows
  • New CI test: urllib_get_no_timeout.py (Linux + Windows matrix entries)
  • CI green

Copilot AI review requested due to automatic review settings June 30, 2026 01:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 WSAPoll with Winsock select() in handle_net_poll and hl_sleep_poll_sockets.
  • Unix: adjust hl_sleep_poll_sockets polling/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.

Comment thread host/src/lib.rs Outdated
Comment thread host/src/lib.rs Outdated
Comment thread host/src/lib.rs

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
danbugs force-pushed the fix/wsapoll-pollout-spin branch 2 times, most recently from c3d7e73 to 9960572 Compare June 30, 2026 06:08
danbugs added 4 commits June 30, 2026 08:16
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 danbugs changed the title fix(host): replace WSAPoll with select() on Windows fix(host): fix Windows networking read-readiness detection Jun 30, 2026
@danbugs
danbugs force-pushed the fix/wsapoll-pollout-spin branch from b6e08b7 to ab58739 Compare June 30, 2026 08:17
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>
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