Skip to content

Adoption triad - #340

Merged
sgerbino merged 14 commits into
cppalliance:developfrom
sgerbino:pr/adoption-triad
Aug 19, 2026
Merged

Adoption triad#340
sgerbino merged 14 commits into
cppalliance:developfrom
sgerbino:pr/adoption-triad

Conversation

@sgerbino

Copy link
Copy Markdown
Collaborator

Resolves #337.

Registration errors now propagate as error codes instead of throwing
through noexcept frames, a failed assign leaves the object unchanged
with the caller owning the rejected fd, self-assign is rejected, and
the family check is parameterized for the internet family. Stream
release now clears the stale remote endpoint cache.
io_uring's local assign accepted any fd without checks and could not
fail; iocp's closed the held socket before association could fail and
left the endpoint caches empty. Both now validate first and seed the
endpoint caches, sharing the POSIX validation helper.
release_socket() nulled the fd without cancel_and_flush, so pending
SQEs could resolve against a recycled fd number after the caller
closed the released descriptor. The acceptor already did this; the
stream and datagram sockets now match.
Adoption validates family and type before touching the held socket,
never mutates the adopted descriptor, and allows assign-over-open by
cancelling pending operations first. release() cancels pending
operations, deregisters, and hands the open descriptor to the caller.
Implemented on every backend.
tcp_acceptor gains assign(), release(), and native_handle();
local_stream_acceptor gains assign() and native_handle(). Adopting a
listening descriptor seeds the local-endpoint cache so accept paths
that derive the address family from it keep working, including for
adopted IPv6 listeners.
Socket wait(write) previously completed immediately and
unconditionally, which busy-spins external flush loops ('retry when
writable') exactly when the send buffer is full — intolerable once
foreign sockets can be adopted. Write waits now probe with a
zero-timeout poll and park until the reactor reports writability;
select snapshots include parked write waits; iocp routes write waits
through the poll reactor. Acceptor wait(write) keeps immediate
completion and is documented as meaningless.
A handle stays bound to its completion port for its lifetime under
the documented API, so a released socket could never be adopted into
an io_context again: re-association failed with
ERROR_INVALID_PARAMETER. Sever the association in release_socket()
with NtSetInformationFile(FileReplaceCompletionInformation), the same
mechanism the wider ecosystem relies on and the reason release
requires Windows 8.1 or later.
…se successes

BSDs complete the handshake against a full accept queue and the
listener then resets the child, so a success whose peer is already
gone is a legal outcome there. The reset is recorded on the socket;
the false-success bug under test reports success with nothing
recorded, so consult SO_ERROR before counting the outcome as false.
Acceptor read and error waits now observe conditions that already
hold when the wait begins: the reactor backends probe at initiation,
and io_uring completes read waits from the multishot delivery queue —
the accept machinery drains the kernel queue as connections arrive,
so a poll on the listener could never report the readiness that
matters. A connection queued before the wait, including one that
predates adopting the listener, completes it everywhere. Acceptor
write waits, which completed immediately on some backends and never
on others, now fail uniformly with operation_not_supported:
writability is meaningless for a listener, and a deterministic error
beats a divergent lie.
The local stream and datagram sockets refused assign() on an open
socket while every other adoptable type cancels, closes, and adopts —
including the local stream acceptor. The front-end guards were the
only difference; the backends already handle replacement. One family,
one contract: pending operations complete canceled, validation
failures leave the socket untouched, and the caller keeps a rejected
descriptor.
A library that owns its socket needs readiness without ceding
ownership; adopting a duplicate gives corosio a descriptor it may
close while readiness still travels through the shared open file
description. The old sketch predated assign() on tcp_socket; the
example is now compiled.
A cancel reaching a wait after dispatch claimed it — or before it
parked — latched a descriptor flag that outlived the op and canceled
the next wait in the same direction under a fresh token. The latch
was redundant: request_cancel() already marks the op, register_op
consults that mark before parking, and delivery decodes it.
A second listen() re-registered the descriptor (failing on epoll) and
on io_uring retired a live multishot arming without cancelling it,
leaving two armings with the retired side closing its deliveries.
Re-listen is now a backlog change only: registration and arming are
skipped when already live. The listen path also purges deliveries a
released descriptor left queued, as the adoption path always did.
The reactor backends reject with errno values every runtime maps to
generic conditions; the Windows WSA equivalents map on some runtimes
only. Normalize the adoption-contract codes the way the wait
contract's already are, and pin the wrong-type rejection code in the
tests.
@cppalliance-bot

Copy link
Copy Markdown

An automated preview of the documentation is available at https://340.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-19 16:49:35 UTC

@cppalliance-bot

Copy link
Copy Markdown

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

Build time: 2026-08-19 16:59:52 UTC

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.81%. Comparing base (05f0464) to head (91c7848).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #340      +/-   ##
===========================================
+ Coverage    79.78%   79.81%   +0.02%     
===========================================
  Files           96       96              
  Lines         5926     5924       -2     
  Branches      1209     1209              
===========================================
  Hits          4728     4728              
+ Misses         851      849       -2     
  Partials       347      347              
Files with missing lines Coverage Δ
...st/corosio/native/detail/epoll/epoll_scheduler.hpp 82.50% <ø> (+0.68%) ⬆️
.../corosio/native/detail/kqueue/kqueue_scheduler.hpp 68.75% <ø> (+0.70%) ⬆️
include/boost/corosio/native/detail/make_err.hpp 57.14% <ø> (ø)
.../corosio/native/detail/select/select_scheduler.hpp 83.33% <ø> (ø)
include/boost/corosio/tcp_acceptor.hpp 91.66% <ø> (ø)
include/boost/corosio/tcp_socket.hpp 87.17% <ø> (ø)
src/corosio/src/tcp_acceptor.cpp 88.23% <ø> (ø)
src/corosio/src/tcp_socket.cpp 82.69% <ø> (ø)

Continue to review full report in Codecov by Harness.

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

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

@sgerbino
sgerbino merged commit a08f4f0 into cppalliance:develop Aug 19, 2026
83 of 86 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Beast2 Aug 19, 2026
@sgerbino
sgerbino deleted the pr/adoption-triad branch August 21, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Complete the assign/release/native_handle triad on the internet family

2 participants