Skip to content

feat(wait): add wait() for socket and acceptor readiness without I/O#251

Merged
mvandeberg merged 1 commit into
cppalliance:developfrom
mvandeberg:pr/246-wait-op
May 19, 2026
Merged

feat(wait): add wait() for socket and acceptor readiness without I/O#251
mvandeberg merged 1 commit into
cppalliance:developfrom
mvandeberg:pr/246-wait-op

Conversation

@mvandeberg
Copy link
Copy Markdown
Contributor

Exposes an asio-style wait(wait_type) awaitable on tcp_socket, udp_socket, local_stream_socket, local_datagram_socket, tcp_acceptor, and local_stream_acceptor. The operation suspends until the descriptor is ready in the chosen direction (read / write / error) without transferring any bytes; useful for wrapping nonblocking C libraries (libssh, libpq async, etc.) that own their own recv/send and only need readiness notification.

POSIX backends share a new reactor_wait_op template that parks in three new wait_*_op slots on reactor_descriptor_state. Dispatch in invoke_deferred_io completes the matching slot on each event bit without performing any I/O syscall.

wait_type::write completes immediately on a connected socket on every backend, matching asio's IOCP contract. Corosio's reactor backends use edge-triggered EPOLLOUT/EVFILT_WRITE; parking would never fire on an already-writable socket, and backpressure is surfaced through write_some()'s return value as usual.

On IOCP, stream-socket wait_read uses a zero-byte WSARecv. All other waits (datagram wait_read, acceptor wait_read, every wait_error) route through a new win_wait_reactor: a dedicated WSAPoll thread woken via a loopback Winsock socket pair, bridging readiness into the IOCP queue via PostQueuedCompletionStatus. The reactor is lazily constructed via std::call_once and stopped early in win_scheduler::shutdown() so parked ops do not block work-counter drain. socket.cancel() / close_socket() / shutdown all route through the same cancel_wait_if_constructed path so reactor-parked ops are cleaned up without forcing reactor construction on hot paths that never used it.

Doxygen on every new public symbol; new Antora guide page (4r.wait.adoc) covers the readiness pattern, acceptor semantics, cancellation, and the wait_type::write immediate-ready contract.

Tests parameterised across backends cover the no-consume promise on TCP, immediate write completion, local_stream wait_read (POSIX), UDP wait_read (exercises the aux reactor on IOCP), cancellation through the standard reactor (wait_read on TCP), cancellation while parked in the aux reactor (UDP wait_read), and acceptor wait_read. wait_type::error is plumbed but unexercised by tests; kernel error semantics are non-portable and the contract is documented as best-effort.

Exposes an asio-style wait(wait_type) awaitable on tcp_socket,
udp_socket, local_stream_socket, local_datagram_socket, tcp_acceptor,
and local_stream_acceptor. The operation suspends until the descriptor
is ready in the chosen direction (read / write / error) without
transferring any bytes; useful for wrapping nonblocking C libraries
(libssh, libpq async, etc.) that own their own recv/send and only need
readiness notification.

POSIX backends share a new reactor_wait_op<Base> template that parks
in three new wait_*_op slots on reactor_descriptor_state. Dispatch in
invoke_deferred_io completes the matching slot on each event bit
without performing any I/O syscall.

wait_type::write completes immediately on a connected socket on every
backend, matching asio's IOCP contract. Corosio's reactor backends
use edge-triggered EPOLLOUT/EVFILT_WRITE; parking would never fire on
an already-writable socket, and backpressure is surfaced through
write_some()'s return value as usual.

On IOCP, stream-socket wait_read uses a zero-byte WSARecv. All other
waits (datagram wait_read, acceptor wait_read, every wait_error)
route through a new win_wait_reactor: a dedicated WSAPoll thread woken
via a loopback Winsock socket pair, bridging readiness into the IOCP
queue via PostQueuedCompletionStatus. The reactor is lazily
constructed via std::call_once and stopped early in
win_scheduler::shutdown() so parked ops do not block work-counter
drain. socket.cancel() / close_socket() / shutdown all route through
the same cancel_wait_if_constructed path so reactor-parked ops are
cleaned up without forcing reactor construction on hot paths that
never used it.

Doxygen on every new public symbol; new Antora guide page
(4r.wait.adoc) covers the readiness pattern, acceptor semantics,
cancellation, and the wait_type::write immediate-ready contract.

Tests parameterised across backends cover the no-consume promise on
TCP, immediate write completion, local_stream wait_read (POSIX), UDP
wait_read (exercises the aux reactor on IOCP), cancellation through
the standard reactor (wait_read on TCP), cancellation while parked in
the aux reactor (UDP wait_read), and acceptor wait_read.
wait_type::error is plumbed but unexercised by tests; kernel error
semantics are non-portable and the contract is documented as
best-effort.
@cppalliance-bot
Copy link
Copy Markdown

An automated preview of the documentation is available at https://251.corosio.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-05-19 19:38:37 UTC

@cppalliance-bot
Copy link
Copy Markdown

GCOVR code coverage report https://251.corosio.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://251.corosio.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://251.corosio.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-05-19 19:46:53 UTC

@mvandeberg mvandeberg merged commit f17d881 into cppalliance:develop May 19, 2026
39 of 40 checks passed
@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.76%. Comparing base (6231492) to head (424c417).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #251      +/-   ##
===========================================
+ Coverage    77.70%   77.76%   +0.06%     
===========================================
  Files           96       96              
  Lines         7292     7264      -28     
  Branches      1787     1775      -12     
===========================================
- Hits          5666     5649      -17     
+ Misses        1108     1104       -4     
+ Partials       518      511       -7     
Files with missing lines Coverage Δ
...boost/corosio/native/detail/iocp/win_scheduler.hpp 62.02% <ø> (+0.13%) ⬆️
include/boost/corosio/tcp_acceptor.hpp 91.83% <ø> (ø)
include/boost/corosio/tcp_socket.hpp 87.17% <ø> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6231492...424c417. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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