czw., 25 cze 2026 o 15:12 Vinnie Falco via Boost <boost@lists.boost.org> napisał(a):
On Thu, Jun 25, 2026 at 4:56 AM Rainer Deyke via Boost < boost@lists.boost.org> wrote:
Pretty sure that's not actually true.
Capy provides an execution model for coroutines which offers one simple invariant:
A coroutine will always be resumed by the same Executor object which was used to launch it.
This should probably be stated in the Capy docs up front.
Along with *why* this guarantee is important and worth trading other things for. Regards, &rzej;
This isn't about Capy vs TooManyCooks. This is Capy vs an entire world of C++ coroutine code that can interact with each other more or less seamlessly.
Capy's invariant, stated plainly above, is the coroutine-native expression of the same invariant which Boost.Asio's execution model maintains. Asio's normative requirement, from
https://www.boost.org/doc/libs/latest/doc/html/boost_asio/reference/asynchro... :
The completion handler shall be submitted for execution as if by performing dispatch(ex2, std::move(f)).
Asio got many things right. Capy takes those things and expresses them with a coroutine-native API. Or as I like to call it: coroutine-ONLY.
Corosio similarly borrows the good parts of Asio unabashedly. And it uses Capy for its execution model. Both libraries, built from Boost.Asio's 24 years of experience getting things right. There's a reason we asked Jeff Garland to be the review manager, as he was also the review manager for Boost.Asio.
"Capy vs the entire world of C++ coroutine code" is actually "Capy continues 24 years of proven Boost design, while the rest of the coroutine world chose the path of least resistance."
And, to be clear, I'm reviewing Capy as a replacement for TMC, not as an extension.
Every misconception, misgiving, and misapprehension can be derived from a failure to understand the Capy execution model.
One could infer that "you don't understand Capy" and I think that's both the charitable reading and also the correct reading. The documentation should make the invariant clear. That's on us. The documentation problem is surfacing in the Boost review, and this is a good thing. That's what Boost reviews are for. We've been steeping in it so long that we can't see it from the outside, for someone encountering it cold.
Now let's review each of your objections through the lens of Capy's execution guarantees (some quotes are paraphrased):
1. "Capy wants to be the only coroutine library." This follows from not understanding why the boundary exists. The boundary enforces the invariant. It's the same reason Asio dispatches handlers through the associated executor instead of calling them directly. You called resume() manually. You called the handler directly. We don't do that.
2. "Capy is too restrictive to be useful." This conflates "I can't bypass the executor" with "I can't do anything." The restriction is the feature.
3. "Plain awaitables should just work." A plain awaitable can resume on any thread via raw coroutine_handle::resume(). That breaks the invariant. The compile error prevents it. You went around it.
4. "The two-call syntax is brittle." The two-phase syntax exists to deliver the frame allocator to operator new before the coroutine body executes. This is what supporting custom allocators looks like, which TMC doesn't offer.
5. "Not enough synchronization primitives." Your shared_mutex sketch calls coroutine_handle::resume() directly on the unlock thread. That breaks the invariant. An async_shared_mutex for Capy would need to dispatch through the executor. it is a reasonable feature request, but your proposed design is unsafe in Capy's model for the same reason it's unsafe in Asio's model.
6. "Not enough executors." You reviewed only Capy, not Corosio, which provides the concrete executors. This is the equivalent to reviewing Asio's executor model without looking at io_context.
7. "resume_on is better than run()." resume_on switches the current coroutine to a different executor without creating a new environment. run() creates a new io_env with the correct executor, stop token, and allocator. resume_on is simpler because it doesn't preserve the invariant across the transition. It trades away environment propagation for simpler syntax. That's a valid choice for TMC's model. It's not compatible with Capy's guarantees.
8. "The awaitable_wrapper can solve this generically." The wrapper calls resume() directly, doesn't dispatch through the executor, doesn't know which thread the foreign operation completes on. It breaks the invariant in exactly the way IoAwaitable prevents.
9. "TMC task runs without an executor." Manually calling resume()/done()/destroy() on a coroutine_handle. Works in a toy example, breaks invariants in production code.
10. "Capy is a trap." A trap implies hidden costs. The cost is explicit: a compile error when you try to bypass the executor. That's the opposite of a trap.
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/QVN4UVUC...