On Mon, Sep 7, 2026 at 6:16 PM Jeff Garland via Boost <boost@lists.boost.org> wrote:
I recommend that *Boost.Capy and Boost.Corosio be conditionally accepted into Boost.*
Thanks, and apologies for my late reply.
From my point of view the review participation was at the minimal tolerable participation level for acceptance. I did my best to try and recruit
external participation to little avail.
Yeah. I wish I could lay the blame elsewhere but I think this points to a deeper problem that our efforts to rally participation numbers in Boost have not succeeded.
In my professional work having helped design and build a coroutine framework using elements of std, asio, beast, and our own. This framework is dramatically less ambitious than these two libraries and built strictly to need.
Ha! Thank you for noticing :)
Acceptance Conditions
Yep. Much work to do.
1. Documentation rework
Yeah. The docs need a lot of work. They are never done.
Several reviewers noted the difficulties with the docs -- especially in understanding the Capy design -- and I concur. It's possible some of these were already addressed as I saw some PRs happening during/after the review. Overall, this is unsurprising to me as async coding is really really hard.
Not just that but documenting coroutines / awaitables in particular comes with its own extra challenges.
...there are at least three audiences:
- ordinary Capy/Corosio users; - authors of higher-level libraries using Capy; - authors implementing the coroutine/environment protocol for their higher level library
The third audience in particular needs normative-level documentation of the rules. If it's there, it didn't really come through for me and clearly for one reviewer that attempted to us Capy directly for his use case.
Agreed. We focused on what we could, and the implementors got the short end of it. Because there are far fewer of them. We will get to it. Still, not bad for just 7 months of work :)
io_result needs to be front and center in the Capy docs
Uhhh... yeah. The io_result is a constant irritant. The compound return values from co_await on I/O operations complicates the ergonomics of it. There is no beautiful solution that we have found. There is also an annoying memory corruption on MSVC compiler versions and coroutines which constrains us there. There's a significant discussion about pulling in various parts of Boost
into Capy when they aren't required.
A primary goal of Capy and Corosio is to become a de-facto standard. That is, the libraries that users turn to when they need networking, full stop. We intend to accomplish this outside the WG21 system, because my research indicates that standardization makes libraries demonstrably worse. In order to become a de-facto standard we have to make sure there is only one version of Capy and only one version of Corosio. If these libraries depend on other Boost libraries, there will naturally be users who are averse to Boost who would prefer a port of these libraries that does not use Boost. At this point, there will be a fork. Two versions each of Capy and Corosio. This works against the de-facto standardization of networking. For this reason, Capy and Corosio are "in Boost" but they do not "require Boost." This allows them to be cloned and used independently, without the Boost superproject, without B2, without Boost.Config, etc... And they work perfectly fine, and they are fully compatible with other libraries that do use Boost, or even use Capy and Corosio. Let's talk about Capy::quitter The point of quitter is to show that authors can implement their own task types. My theory, is that there is no one universal task type. There are uncountably many task types, each suited to their domain, with different behaviors. In the Capy (P4003) model, what they all have in common is the io_env, the stop_token, and the frame allocator. Besides that, they can diversify. quitter shows that a Task-like awaitable can have different cancel semantics while still integrating into the Capy ecosystem. 2. Overall Design - Splitting the Libraries
The consensus seems to be that the separation of the libraries makes sense and I concur. However there are a number of detailed questions about the location of some facilities such as timing primitives that should be addressed. There's also an argument to break things down even further to allow reuse of useful synchronization primitives depending on all of Capy.
If it was up to me I would decompose Capy further. But doing so is likely to alienate more users than it helps. C++ has a weird ecosystem compared to other languages. In Python, Node, Rust, etc... no one blinks an eye at adding fine-grained dependencies. But in C++ well, many people don't want any dependencies at all! Capy and Corosio actually started out as 6 separate libraries (lol). The current split is a compromise between what is political feasible and what is technically superior. In terms of what goes where, the dividing line is actually quite bright: 1. Corosio is for things which require a platform reactor. 2. Capy is for everything else. A consequence is that Capy is pure-std. Pure cross-platform code. While Corosio is portable wrappers around an operating system facility (the reactor / async event loop).
*1a* Corosio: Delays and Timeouts There was discussion on this point including why this was a Corosio primitive and not in Capy.
Quite simply, the previously stated principle. Timers require a platform reactor.
*1b* Capy thread_pool
What's wrong with boost.thread thread_pool was asked somewhere? Well really
this type isn't so much a thread pool as an execution context (the base class). I'd suggest renaming to thread_pool_context or threaded_context clears up the confusion here.
The name is actually capy::thread_pool, not thread_pool. This difference is the distinction.
*1c* Adding to Capy - io_context and signal handling?
From my professional experience writing servers purely with coroutines the io_context is the key to making signals, networking, file-io all go together smoothly. As such it seems like that fundamental construct should be in Capy.
Same principle. Signals requires a reactor. It is a platform facility with a portable wrapper, these belong in Corosio.
Let's take a use case of writing a command line tool to read a directory of files where I read and run a regex across each one to see if there's a match. Of course the user should be able to interrupt cleanly at any time. So we need a signal handler and a stop token to cancel work. Right now I think I have to use Corosio to do this when it seems this should be Capy only.
LOL... think about what you are asking. File operations are typically synchronous and so is regex. So the naive implementation would have exactly one place to poll the stop token: in between the dir command and the regex command. Unless you are proposing writing a fully async DIR and a fully async regex that is interruptible? Interruption implies concurrency, because the cancel signal has to come from a foreign thread. It can't come from the thread doing the work; that thread is busy doing the work. And with concurrency you need to use asynchronous operating system APIs, and this requires a reactor. That lands in the purview of Corosio.
Overall handling signals in a coroutine server is pretty essential and seems to be roll-your-own awkward for Capy.
Capy is portable, and signals are platform specific. -> Corosio. The Corosio io_context is really a simplification of the asio io_context the
traffics directly in coroutine handles AFAICS. I'm unsure if this means Corosio's io_context should be redesigned and moved or if Capy should just have an independent facility. Of course feel free to explain why this isn't needed at all.
If Capy gets the facility then there is no distinction between the libraries and we have collapsed Capy and Corosio down to a single library. The split is proven; users are using Capy without Corosio. Bright line again. Capy is pure std-C++, Corosio brings the platform reactor. *1d* splitting Capy into more parts
I think there's an argument for this going forward, but I wouldn't push for this in particular right now. There are things like Capy::quitter
Capy::quitter is just an example though, not a public API. 3. The buffer problem
I'm sure the following will cause a substantial amount of potentially harsh discussion (please start a separate email chain). However, I'm unpersuaded that the current design handles the buffer abstractions and buffer concepts correctly.
Agreed. The buffers are a headache with no perfect answers. Happy for you to collaborate on helping to explore the design space.
These two types are actually minimalist versions of std::span. They are pointers and size to the actual buffers with data. So for me at least this is an api usability issue:
Agree with the usability issues in general, this is an area where Capy needs work. But before we go running to span, perhaps give this a read: *The Span Reflex* *When Concrete Thinking Blocks Compositional Design* https://www.vinniefalco.com/p/the-span-reflex-when-concrete-thinking
My future desire would be that Capy and Corosio continue exploring the concepts and factoring needed to provide greater interoperability with other libraries.
This unfortunately is one of those problems which happens to have a perfect solution via the standardization process. Buffer sequences are the ideal candidate for a vocabulary type. And Asio gets the buffer sequences largely correct, once we roll back the changes that came about from various WG21 meetings trying to move the Networking TS forward. Many thanks for your generous investment of time in the review process. Best