sob., 27 cze 2026 o 14:22 Vinnie Falco via Boost <boost@lists.boost.org> napisaĆ(a):
This review has surfaced a documentation problem that I want to address directly. Several reviewers have arrived at different (incompatible) conclusions about what Capy is, because we never stated it plainly. Let me try now.
*What Capy is*
Capy is a proposed standard protocol for coroutine environment propagation, plus a reference implementation of that protocol.
The protocol (IoAwaitable) ensures that three things flow correctly through co_await chains: executor, stop token, and allocator. The concrete library - thread pool, task types, byte streams, synchronization primitives - exists to prove the protocol works in practice.
Actually, I do not think I understand this statement. What does it mean when you call the concept `IoAwaitable` a protocol? 1. What is this concept for? Are programmers/other libraries allowed/expected to provide their own types modelling `IoAwaitable`? Or does anyone is required to only use concrete types that come with Capy? 2. If it is the latter, why are we even hearing about this protocol? If it is the former, then I think the "protocol" is way underspecified. 3. I can write my own coroutine return type that has two-argument await_suspend, but lacks await_ready and await_resume. It will pass the IoAwaitable check, but I cannot use it inside a coroutine with capy::task as a return type. Apparently, there are more syntactic requirements here. 4. And IoAwaitable doesn't mention any semantic requirements. Concepts without semantic requirements are not concepts. 5. Is it enough for my type to satisfy the concept or is there more that I have to do? How do I make `co_await this_coro::environment` work? Is it even part of the "IoAwaitable protocol"? How is the environment propagated down to other people's coroutines (with their own return types) unless I manually make it work? 6. What happens with my promise_type's initial_suspend and final_suspend? Are they invoked or overridden by capy::tasks's await_transform? I hope that questions demonstrate how unclear it is to the reader what "IoAwaitable protocol" is. Regards, &rzej;
*The invariant*
Capy enforces one rule:
A coroutine always resumes on the executor it was launched with. This isn't a restriction for its own sake. Consider:
capy::task<void> handle_client(connection& conn) { auto req = co_await conn.read(); auto resp = process(req); co_await conn.write(resp); conn.stats.requests++; }
Launch this on a strand. Every resumption after co_await happens on that strand. conn.stats.requests++ is data-race-free without a mutex. Correct by construction.
Without the invariant, after co_await conn.read() you might resume on an io_uring completion thread, a random pool thread, anywhere the I/O subsystem completed. Now you need either a mutex around every access to shared state, or a manual co_await resume_on(my_strand) after every co_await. The first defeats strands. The second is verbose, repetitive, and if you forget one, you have a silent data race.
The invariant makes the correct thing automatic and the incorrect thing a compile error.
*Why IoAwaitable, not plain awaitables*
A plain awaitable can resume a coroutine on any thread by calling coroutine_handle::resume() directly. That breaks the invariant. The compile error when you co_await a plain awaitable from a Capy coroutine is the type system preventing this.
This isn't about locking you in. It's about making environment propagation explicit. An IoAwaitable receives the io_env (executor + stop token + allocator) and promises to dispatch the resumption through the executor. That promise is what enables the safety guarantee.
*The interop problem*
Without a shared protocol, N coroutine libraries need N*(N-1) adapters. With a standard protocol for environment propagation, one bridge covers everyone. IoAwaitable is our proposal for that protocol. Capy is the testbed. The papers (P4172, P4092, P4093) are the standardization path.
*What Capy is not*
Capy is not a general-purpose "do everything" coroutine framework competing with TooManyCooks feature-for-feature. It is not an implementation detail of Corosio. It is the execution model and byte-stream layer - usable standalone for business logic that operates on streams without platform I/O (HTTP parsing, protocol state machines, serialization), and usable as the foundation for Corosio's networking layer.
CERN's traccc project uses Capy without Corosio for GPU reconstruction pipelines. The Boost.HTTP parser is implemented entirely on Capy's byte streams. These are the intended use cases for Capy alone.
*What went wrong in this review*
The documentation never stated any of the above. It explained the API without explaining the design's purpose. Rainer encountered a type-safety check, found no documentation for the escape hatch, and concluded the escape hatch didn't exist. The frustration was understandable. The documentation failed him.
We are fixing this.
Bikeshed ideas: Cowaitable, Exwaitable
Thanks _______________________________________________ Boost mailing list -- boost@lists.boost.org To unsubscribe send an email to boost-leave@lists.boost.org https://lists.boost.org/mailman3/lists/boost.lists.boost.org/ Archived at: https://lists.boost.org/archives/list/boost@lists.boost.org/message/XTGEPSMU...