Repository navigation
feat(xpp): async DNS + hostname-aware TCP/UDP - #204
Merged
Merged
Conversation
Three layered changes:
1. spawn_blocking + blocking pool fix. worker_main now consumes the
N async threads, while xTaskGroup has +512 headroom (matches
Tokio's thread_cap default) so blocking work expands the pool on
demand instead of dead-locking. Inlines the adapter into a single
BlockingSpawnTask, cutting per-call allocations from 4 to 2.
2. Async DNS via lookup_host("host:port"). Wraps getaddrinfo in
spawn_blocking — same pattern as tokio::net::lookup_host. Accepts
IPv4 / IPv6 / hostname literals.
3. TcpListener::bind / TcpStream::connect take a "host:port" string
and resolve via DNS, trying each resolved address in order.
The pre-parsed-SocketAddr fast path is renamed to bind_addr /
connect_addr. Both return Promise<Result<…, SocketError>>.
Also: AdapterPromiseNode::m_val now wraps T in Option so non-default-
constructible payloads (like Result<T,E>) can flow through Promise
chains; SocketError gains ResolveFailed and NoAddress.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up cleanup on the async DNS / TCP work: - TcpStream::connect_addr and TcpListener::accept now return Promise<Result<…, SocketError>>, replacing the empty-stream sentinel. connect_each in the hostname path becomes a clean Result chain. - UdpSocket::bind / connect get matching "host:port" overloads; the synchronous SocketAddr fast-paths move to bind_addr / connect_addr (parallel to TCP). - New SocketError::Closed for operations on closed sockets (set_nodelay, nodelay, accept on a closed listener). - SocketAddr::unspecified() helper replaces five copies of SocketAddr::from(SocketAddrV4::from(UNSPECIFIED, 0)). - lookup_host's host buffer is NI_MAXHOST instead of 256, so any RFC-compliant DNS name (max 253) fits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Linux fast path declared `int fd = ::socket(...)` inside `#ifdef __linux__`, then the fallback path re-declared `int fd` unconditionally — legal on macOS (the first decl is preprocessed out) but a redeclaration error on Linux. Move the single declaration above the #ifdef. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous version stashed N concurrent lookup_host promises in a Vec, then awaited only the last and let the others detach. On single-core Linux CI runners the test hung past the 60 s ctest timeout — the detached-promise + main-loop done_fn timing has a genuine ordering issue worth investigating, but the test itself is not the right place to assert blocking-pool concurrency. Switch to four sequential lookups. Each is a separate spawn_blocking submission, which is what we actually wanted to exercise; concurrent variants are covered by RuntimeTest.SpawnBlockingDoesNotStarveWorkers (coroutine path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
xpp::net::lookup_host("host:port")— wrapsgetaddrinfoinspawn_blocking, mirrorstokio::net::lookup_host. Accepts IPv4/IPv6 literals and hostnames in one string.TcpListener::bind/TcpStream::connectandUdpSocket::bind/UdpSocket::connecttake a"host:port"string and resolve via DNS, trying each address in order. Synchronous fast paths move tobind_addr/connect_addr.Runtime::spawn_blocking+ a Tokio-style 512-thread blocking-pool headroom onxTaskGroupto prevent dead-lock when async workers occupy every slot.BlockingSpawnTaskkeeps allocations minimal (2 heap allocs per call instead of 4).AdapterPromiseNode::m_valnow storesOption<T>so non-default-constructible payloads (Result<T,E>) can flow throughPromisechains.Error model
All
connect/bind/acceptpaths returnPromise<Result<…, SocketError>>. The empty-stream sentinel thatconnect/acceptpreviously used to signal failure is gone. New error variants:ResolveFailed,NoAddress,Closed.Test plan
build/xpp_test: 649 pass, 0 failbuild-cxx20/xpp_test: 680 pass, 0 failbuild-cxx20/xpp_coro_test: 31 pass, 0 failDnsTest(8 cases: IPv4/IPv6 literals, localhost, bogus hostname, malformed inputs, blocking-pool concurrency)TcpHostnameTest(9 cases: bind/connect by host:port, IPv6, accept Result, closed-listener)UdpSocketTestBindHostPort*/ConnectHostPort(4 cases)RuntimeTest.SpawnBlocking*(7 cases incl.DoesNotStarveWorkers— regression test for the dead-lock fix)RuntimeTest.SpawnBlockingAwait*coroutine variants (7 cases)Notable behavior changes
TcpStream::connect(SocketAddr)is nowconnect_addr(SocketAddr) -> Promise<Result<TcpStream, SocketError>>. The freeconnectname is reserved for the hostname overload.TcpListener::bind(SocketAddr)→bind_addr(SocketAddr). Same renaming forUdpSocket.TcpListener::accept()returnsPromise<Result<TcpStream, SocketError>>instead ofPromise<TcpStream>.🤖 Generated with Claude Code