On Thu, Jun 25, 2026 at 12:32 AM Rainer Deyke via Boost < boost@lists.boost.org> wrote:
Add a member to your awaitable of type continuation and set the coroutine handle. Then pass it to dispatch.
So I can just leave the 'next' member as nullptr, and everything works? That's not clear from the documentation at all. That would have been helpful to know three weeks ago.
Right.
I know it's deliberate. That doesn't make it any less restrictive.
Capy is being evaluated here by itself, outside the context of Corosio, and for a use-case it doesn't claim. By itself, Capy is quite capable (hence the name). It offers byte-oriented I/O on optionally type-erased streams, and quite a lot of business logic can be implemented with this. For example, this library is entirely implemented with Capy (not Corosio). Here is the HTTP parser: https://github.com/cppalliance/http/blob/571adcc96ad1f2df935ebbfdf9cb76a20b7... This function operates on an asynchronous (or synchronous) byte-oriented stream. It does not require Corosio. No sockets, no platform-specific APIs. That is why Capy is separate (as previously stated). This is also the pattern that Beast2 will use. Most business logic implemented on type-erased streams which have nothing to do with sockets, pure Capy. Capy and Corosio are a single submission split into two physical libraries. This review evaluated one half as a standalone general-purpose coroutine library and found it too restrictive for a use case it does not claim.
I like my approach better, although I don't know how viable the to_io_awaitable function is. Or why Capy doesn't provide it, if it is.
Capy doesn't provide to_io_awaitable() because it can't be implemented correctly for arbitrary awaitables. The wrapper would have to call resume() on the right executor in order to maintain the execution guarantees, and it can't know which executor that is without domain knowledge about how the foreign operation completes. That domain knowledge is what the author of an IoAwaitable provides. You write a "leaf awaitable." This is explained in Section 3.3: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p4172r1.pdf The compiler error is the principled choice.
Awaiting an awaitable is an implementation detail of the function 'f' here. It should have no effect on the function signature.
Yes, we agree. The leaf IoAwaitable is the answer here (described above).
...this looks a lot like the chain of callbacks that coroutines were invented to avoid.
`await_sender` usage (from the bridge paper): auto result = co_await await_sender( ex::on(gpu_sched, ex::then(ex::just(data), classify)));
..innocent-looking functions like the following do not work correctly:
This is not quite right. Your `task` coroutine frame is allocated using the default allocator. Suboptimal, but it is what you asked for. The launch function works fine. The coroutine executes. The coroutine frame is deallocated properly. The invariants are preserved. ...I am allowed to prefer TooManyCooks' approach, and I am allowed to say
so in my review.
And I would point out that TMC doesn't have this problem, because TMC doesn't have the feature. TooManyCooks does not support customizing the frame allocator. It uses global ::operator new unconditionally, and the guidance is to "link with tcmalloc, mimalloc, or jemalloc instead of the default glibc malloc." If you don't have one of these the CMakeLists.txt will even warn you. The two-phase syntax is the price of a feature TooManyCooks doesn't offer. Capy follows in Boost's original tradition of pioneering solutions.
I lose the ability to get a result back to the coroutine, but I was never going to be able to do anything useful with that result anyway because boost::capy::run_async swallows return values. If I want to use the result locally instead of returning it, I can do so within the callback.
I'm getting a little lost because you are first talking about capy::run(), then capy::run_async(). The two functions serve different purposes. capy::run_async() is "fire and forget". The caller indicates they are disinterested in the result. If they were interested in the result, then they would be expressed as a coroutine, which uses co_await, and either awaits the task directly or awaits a call to capy::run(). Please help me understand what the actual issue is. Thanks