Wait readiness probe - #336
Conversation
The reactor backends decided socket wait() completion from cached edge events, which fail in both directions on edge-triggered backends: - False negative: a short read completes without hitting EAGAIN and consumes the readiness edge with no trace; a subsequent wait(read) parks forever although data remains buffered, because EPOLLET/ EV_CLEAR never re-fire without a new transition. - False positive: an edge arriving with no op parked latches the sticky read_ready flag; a speculative read then drains the socket without consuming the flag, and a subsequent wait(read) completes immediately on an empty socket. The select backend latches the same stale flag despite being level-triggered. Decide wait completion by asking the kernel instead: probe with a zero-timeout poll() at initiation (an edge-triggered reactor cannot report a condition that already holds), re-probe when register_op consumes a cached ready flag, and re-probe at event dispatch before completing a parked wait op, keeping it parked on EAGAIN. The probe is side-effect free; in particular it never reads SO_ERROR, which is consume-on-read and belongs to whichever operation observes the failure next. The wait(write) immediate-completion short-circuit is unchanged.
The file mixed three guard patterns: a body-guarded test compiling to an empty function on Windows, two definition regions split by a portable test, and two matching #if blocks in run(). The whole suite registers nothing on Windows, so the guards exist only for compilability; use one scheme: whole definitions guarded, one contiguous POSIX region, one POSIX block in run().
macOS timed out both reactor suites: zero-length datagram sends may be rejected there (nothing queued, so the wait parks), and POLLHUP visibility after the reporting read consumed SO_ERROR is a Linux guarantee. Detect both capabilities at runtime and skip the wait leg when absent instead of hanging; Linux still enforces the full regression witness. The raw poll() check also moves both tests into the POSIX region.
A connect completion decided from SO_ERROR alone reports success for a handshake still in flight: an in-progress connect also reads 0. The check is reachable without a genuine post-handshake event — fresh TCP sockets raise a spurious writable edge at registration, so a loop iteration between open() and connect() latches a stale write_ready that register_op consumes mid-SYN_SENT, and the same stale edge can reach a parked connect through dispatch. Probe writability with a zero-timeout poll() first and report EAGAIN to stay parked; SO_ERROR decides only once the socket is actually writable. The dispatch connect arm re-parks on EAGAIN like every other op instead of completing unconditionally. The regression test holds a connect in SYN_SENT deterministically (backlog-full loopback listener drops the SYN) and drains the ready queue before cancelling so a falsely posted completion cannot be masked as canceled at delivery.
|
An automated preview of the documentation is available at https://336.corosio.prtest3.cppalliance.org/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-08-17 12:43:47 UTC |
|
GCOVR code coverage report https://336.corosio.prtest3.cppalliance.org/gcovr/index.html Build time: 2026-08-17 12:58:32 UTC |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #336 +/- ##
========================================
Coverage 79.82% 79.82%
========================================
Files 96 96
Lines 5937 5937
Branches 1209 1209
========================================
Hits 4739 4739
Misses 851 851
Partials 347 347 Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
No description provided.