Windows: `Poll::poll` timeout is extended by system suspend
Summary
On Windows, time spent suspended is not counted towards the relative timeout passed to GetQueuedCompletionStatusEx (https://learn.microsoft.com/en-us/windows/win32/fileio/getqueuedcompletionstatusex-func). As a result, Poll::poll can remain blocked after resume even when the caller's Instant-based deadline has already passed.
We encountered this through Tokio and have a deterministic reproduction in tokio-rs/tokio#8344. Microsoft documents that, since Windows 8, the timeout does not include time spent in low-power states.
We are not Windows power-management experts, and it is not entirely clear to us whether Mio intends the timeout to represent active time or elapsed monotonic time. In practice, Tokio derives it from an Instant deadline, and in our reproduction Instant advances while the machine is suspended.
Considered approaches
Tokio could cap every poll timeout and wake periodically, but that adds otherwise unnecessary wake-ups.
Using a Windows waitable timer also seems possible, though integrating and cancelling it through IOCP appears considerably more involved.
Possible direction
One potentially smaller approach is registering for suspend/resume notifications with PowerRegisterSuspendResumeNotification (https://learn.microsoft.com/en-us/windows/win32/api/powerbase/nf-powerbase-powerregistersuspendresumenotification). On resume, the callback could post a private completion to Mio's existing IOCP. Mio would filter that event and recalculate the remaining timeout from its original Instant deadline.
This would require no periodic wake-up, helper thread, or public API change.
Would this be an appropriate direction for Mio, or should the timeout continue to follow the underlying Windows active-time semantics?
Environment
Environment that we experimented on:
- Windows 11
- mio 1.2.2
- tokio 1.53.1
Prior Art
Go registers for Windows suspend/resume notifications, wakes sleeping runtime threads, and recalculates elapsed monotonic time after resume: https://github.com/golang/go/blob/master/src/runtime/os_windows.go
libuv already uses Windows callbacks to post private completions into its IOCP event loop: https://github.com/libuv/libuv/blob/v1.x/src/win/tcp.c
Source: tokio-rs/mio