Coroutine-native networking. Every operation is a co_await. No callbacks, no adapters, no surprises.
Callbacks require a class, shared ownership, and a state machine. Coroutines require a function.
// Each async step is its own function. // Shared state needs shared_ptr to stay alive. // "Retry on failure" requires a state machine. struct Request : std::enable_shared_from_this<Request> { tcp::socket socket_; tcp::resolver resolver_; tcp::timer timer_; std::string host_, body_; char buf_[4096]; int retries_{ 0 }; void run() { do_resolve(); } void do_resolve() { auto self = shared_from_this(); resolver_.async_resolve(host_, "80", [self](error_code ec, tcp::resolver::results_type r) { if (ec) return self->retry(ec); self->do_connect(r); }); } void do_connect(tcp::resolver::results_type eps) { auto self = shared_from_this(); async_connect(socket_, eps, [self](error_code ec, tcp::endpoint) { if (ec) return self->retry(ec); self->do_write(); }); } void do_write() { auto self = shared_from_this(); async_write(socket_, buffer(body_), [self](error_code ec, size_t) { if (ec) return self->retry(ec); self->do_read(); }); } void do_read() { auto self = shared_from_this(); socket_.async_read_some(buffer(buf_), [self](error_code ec, size_t n) { if (ec) return self->retry(ec); process(self->buf_, n); }); } void retry(error_code ec) { if (++retries_ > 3) return on_fail(ec); timer_.expires_after(seconds(retries_)); timer_.async_wait( [self = shared_from_this()](error_code) { self->do_resolve(); }); } };
// No class. No shared_ptr. // Retry is just a loop. // Reads exactly like what it does. capy::task<> fetch( corosio::io_context& ioc, std::string host, std::string body) { for (int attempt = 1; attempt <= 3; ++attempt) { corosio::tcp_socket sock(ioc); corosio::resolver res(ioc); auto [rec, eps] = co_await res.resolve(host, "80"); if (rec) { co_await sleep(ioc, seconds(attempt)); continue; } auto [cec] = co_await sock.connect(eps); if (cec) { co_await sleep(ioc, seconds(attempt)); continue; } auto [wec, wn] = co_await capy::write(sock, buffer(body)); if (wec) { co_await sleep(ioc, seconds(attempt)); continue; } char buf[4096]; auto [ec, n] = co_await sock.read_some(buffer(buf)); if (!ec) { process(buf, n); co_return; } } }
"An API designed from the ground up to use C++20 coroutines can achieve performance and ergonomics which cannot otherwise be obtained."— Vinnie Falco, Author
Every operation returns an awaitable. No callbacks, no handler layer.
co_await s.connect(ep)Errors are values. Call .value() to throw instead.
auto [ec, n] = co_await ...Propagates through await_suspend. Deterministic. No surprises.
Thread-local recycling pools. Zero frame allocs at steady state.
Single awaitable handshake wraps any TCP socket.
co_await tls.async_handshake()io_context, socket, acceptor, server, resolver, timer, signal_set.
Stop tokens flow forward from launch alongside execution context.
Unit-test I/O without a network. Drop-in Mocket.
boost/corosio/mocket.hppCorosio is the networking layer. Capy is the I/O foundation — executor model, task types, buffer sequences, cancellation.
A derivatives exchange is porting from Boost.Asio to Corosio. These are the engineers' own words after two weeks of integration work.
Source: P4125R0 — Field Experience: Porting a Derivatives Exchange to Coroutine-Native I/O
Build something, review the library, share feedback, or just follow along.
Battle-test Corosio before the formal Boost review. 2–6 hours. Your name on the founding reviewers page.
Built something with Corosio? Open a GitHub Issue with the Showcase template and we'll feature it.
Submit Your Project →Working code in a public repo
What problem Corosio solved
Performance or ergonomics notes
API friction you encountered
Find us on the C++ Language Slack in #boost-beast2, or follow progress on GitHub.
Join C++ Language Slack →