Skip to content

Conversation

@PyXiion
Copy link
Contributor

@PyXiion PyXiion commented Jan 25, 2026

The purpose of this PR is to simplify the network API on the road to 1.0.0 #395.

This is mainly to gather feedback on the API shape, especially around
io_status and error handling. udp_peer, tls_client, and others are
not implemented in this PR yet.

Why:

  • Reduced boilerplate: Removes the need to manually manage poll(). write_all and read_exact handle timeouts internally.
  • Unified error handling: Unifies different enums into a single rich object (io_status)
  • Removing epoll-style readiness semantics: the previous API leaked a readiness-based design (poll() + recv/send), which works for epoll/kqueue but does not work with completion-based backends like IOCP (Windows) and io_uring

Key changes:

  • Read/Write API: encapsulates poll() logic

    • read_some/write_some: Asynchronously waits for the socket to be ready and performs a single read/write operation
    • read_exact/write_all: High-level helpers that loop until the entire buffer is processed or an error/timeout occurs
  • Unified io_status

    • It carries noth high-level kind (e.g. ok, timeout, closed) and the native_code (errno, WSAGetLastError for Windows in the future)
    • Provides human readable .message()
    • Provides concise is_ok(), is_timeout() checks instead of status == coro::net::recv_status::some_status
  • The sync accept() renamed in accept_now(). The new accept() is asynchronous.

  • Using std::span<const std::byte> and std::span<std::byte>. Easy integration with std::as_bytes and std::as_writable_bytes

Examples:

Reading

// Before
auto pstatus = co_await client.poll(coro::poll_op::read, timeout);
if (pstatus != coro::poll_status::ok) {
  handleError(pstatus);
  co_return;
}
auto [rstatus, span] = client.recv(buf);
if (rstatus != coro::net::recv_status::ok) {
  handleError(rstatus);
  co_return;
}
processData(span);
// After
auto [rstatus, span] = co_await client.read_some(buf, timeout);
if (!rstatus.is_ok())
{
  handleError(rstatus.message());
}
processData(span);

Writing

// Before
std::span<const char> remaining = buf;
do
{
    auto [send_status, r] = client.send(remaining);
    if (send_status != coro::net::send_status::ok)
    {
        co_return; // Handle error
    }
    
    if (r.empty())
    {
        break;
    }

    remaining    = r;
    auto pstatus = co_await client.poll(coro::poll_op::write);
    if (pstatus != coro::poll_status::write)
    {
        co_return; // Handle error.
    }
} while (true);
// After
auto [wstatus, span] = co_await client.write_all(buf, timeout);
if (!wstatus.is_ok())
{
  handleError(wstatus.message());
}

New client

// Before
auto poll_status = co_await server.poll();
if (poll_status != coro::poll_status::read)
{
    co_return; // Handle error
}

auto client = server.accept();
if (!client.socket().is_valid())
{
    co_return; // Handle error.
}
// After
auto client = co_await server.accept(); // std::expected<client, io_status>
if (!client || !client->socket().is_valid())
{
  handleError(client.error());
}

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.

1 participant