[Capy/Corosio] Review

Jamie Allsop via Boost <[email protected]> Fri, 3 Jul 2026 00:26:41 +0100
Newsgroups gmane.comp.lib.boost.devel
Message-ID <[email protected]>
Hi All,

This is my review of the Capy and Corosio libraries. In order to retain 
a structured response I have used the review manager’s questions as 
headings and completed the review accordingly.

Disclaimer: My company has been doing a joint R&D project with the C++ 
Alliance which was driven by our desire to explore the viability of 
migration to a coroutine-first execution model.

Context and Background: I have been using Capy/Corosio, tracking the 
develop branch, in a large, multi-repository project on and off for the 
past 4 months. This is a very mature codebase that has years of 
production use as tier-1 market infrastructure, which relies on 
ultra-low latency and resilience. It is a message passing acrhitecture 
that relies (as you might expect) heavily on networking. In my opinion 
this represents a great test-bed in which to try out Capy and Corosio in 
a real-world setting. To place this codebase into more context it 
comprises about 40 repositories all of which are impacted by this work. 
Like Capy/Coroiso we adopt a layered design (a la Lakos and Large Scale 
C++ - if you haven’t read his original book on this you really should) 
which helps makes this kind of refactoring achievable.

This review is not a detailed dissection of Capy/Corosio, but rather 
more of an experience report. The reason for that is the (relatively) 
long term use of the libraries means that any specifics that caused 
trips or missteps, or that needed adjusted, fixed or explained, has 
happened in the past. What I am offering in the review then is my candid 
opinion based on my experience to-date in the context of delivering a 
business critical platform.


1.  What is your evaluation of the usefulness of the libraries?
---------------------------------------------------------------

Both libraries are useful and although Capy could in theory be a subset 
of Corosio it makes sense to separate them out so that other libraries 
that build on the capabilities exposed by Capy can be developed.

A quick review of the documentation reveals a number of such libraries, 
planned or otherwise:

  - Corosio — portable coroutine networking (this review)
  - Http — sans-I/O HTTP/1.1 clients and servers
  - Websocket — sans-I/O WebSocket
  - Beast2 — high-level HTTP/WebSocket servers
  - Burl — high-level HTTP client

Note to the authors - I’d like to see links to these libraries in the 
docs - even if that directs to a “coming soon” page or repo under 
development. For our needs we’d definitely be looking to utilise 
WebSocket functionality that allows us to replace (or integrate with) 
our existing Beast-dependent code.

Let me note here that I think the separation (Corosio builds on Capy) is 
valuable because it helps people reason about the more general utility 
of Capy. One issue Asio always faced was that people mistakenly assumed 
that the executor model used there was “Asio-only” or somehow only 
relevant to I/O and networking which of course it wasn’t. It just 
happened to be part of the most widely deployed library that needed such 
a model, and the idea was that once standardised it could use that as an 
implementation (conceptually since you wouldn’t change something that 
wasn’t broken).

By having a separate Capy library that encapsulates these core concepts 
I think it is easier to rationalise about the value and utility, and 
allows a lighter weight dependency for users wishing only to access 
those features.

Let me come back to the question of usefulness. Of course the libraries 
are useful. I’d argue they are essential, so I think that’s an easy one 
to answer.

However, the more subtle question is do we need these libraries, which 
at a superficial glance look a lot like some other libraries that 
already exist in Boost, or at least overlap some of the functionality. 
For me I’d say the answer is yes, and while the documentation puts 
forward its rationale for this I’d say that my summary here is that 
Boost is (among other things) about incubating and evolving patterns of 
best practice within a domain or subset of the design space in that domain.

To that end I think Capy and Corosio are well timed examples of what a 
coroutine-first approach to executors and related facilities like 
streams and networking should look like in the context of modern C++, 
and should prove to be an excellent go-to for users new to this kind 
work, especially those familiar with so-called async-style approaches in 
other languages.

I am aware of the conversation in other reviews with respect to Capy, 
and like the fact the authors recognised the disconnect in presentation 
provided by the documentation and their commitment to address that. For 
me I already largely held the views expressed and this is not a mental 
hurdle I have struggled with, but I agree that the documentation should 
be more explicit on these points.

One last point on the usefulness of the two libraries. We’ve had 
coroutines for a while in the language but no compelling libraries that 
(attempt to) showcase their value. In my view that’s a problem. Why? 
Well it means we’ve had a number of years pass without any real 
constrained feedback on the feature itself. By creating libraries that 
help establish a vocabulary, and patterns of use, for coroutines we can 
both better evaluate coroutines in the language within a well defined 
context that provides broad value to the C++ community, while also 
helping iteratively explore the libraries, and resultant vocabulary, 
themselves.

This is what Boost is for, so at a fundamental level (and assuming good 
enough) I welcome the proposal to bring these libraries forward for 
addition. Let’s now see if the rest of my review supports that.


2.  What is your evaluation of the design?
------------------------------------------

The design does a good job of retaining, as far as possible, the 
concepts and ideas developed and established by Asio over the years. 
 From my experience of using the libraries this makes it both familiar 
and accessible. Refreshingly the authors haven’t fallen into the trap of 
trying to create something completely new by discarding decades of 
hard-earned experience from developing high quality systems in Asio, and 
seem to have reflected much of the inherent wisdom captured in the 
evolution of that design.

Personally I care about this as I spent many, many hours in 
conversations regarding the use and evolution of Asio as part of the 
wider efforts to bring that library into the C++ standard. Aside from 
that I have been using Asio for almost two decades as the core execution 
model and networking facility in market infrastructure, with some of 
that infrastructure underpinning the global economy throughout the most 
turbulent times in modern financial markets.

My point? In short, I am acutely aware of the many tweaks and 
modifications applied to Asio over the years to evolve its design and 
capabilities to address real world problems, problems that genuinely 
matter, and where the cost of following stubborn or hypothetical 
opinions are both unacceptable and have significant ramifications.

This matters because I’d strongly argue that “what Asio does” is an 
important benchmark. I’m not talking about some idiosyncratic choice of 
language design like using inheritance where aggregation would do, or 
members versus free functions. Backwards compatibility and an evolving 
language mean we absolutely can, and should, do some things differently 
in newer libraries, and from what I can see that appears to be the case 
in the design of Capy and Corosio. Personally I see this approach as a 
strength and it is probably the main reason I considered looking at the 
libraries as candidates for use in our codebase. If not for the clear 
tie-in to the Asio models I would not have touched this.

Focusing now on the libraries in question, I can say that I appreciate 
the effort that has gone into the libraries to provide a first-class 
experience with coroutines in C++, and while that might not be 
everyone’s ideal (coroutines to underpin networking etc) it is clearly 
and unashamedly the key goal of these libraries. If you need or want a 
broader set of facilities then Asio is already there, but if you want to 
be coroutine-first then this is what you want. Importantly, to reiterate 
my earlier point, this also give us as users a choice, and as a 
community, a valuable vehicle in which to more concretely explore the 
coroutine support we have in the language. I think in the long term we 
will all benefit from that.

On the more general topic of design I have not observed any gaps in what 
we require in our codebase, or significant red flags. There have been 
some small gaps over the 4 month period but those have been closed in a 
timely manner and are the kind of things you’d expect from a library 
under development and refinement. I can say that for the time period 
that I have been tracking the libraries the design has continued to 
evolve towards the libraries stated goals.


3.  What is your evaluation of the implementation?
--------------------------------------------------

I’ll answer this from two perspectives:

1. What would I care about if I was forced to find an issue in the code, 
or needed to read the source to really understand what’s going on? and,

2. Does the implementation appear to be of high quality?

For 1: I haven’t spent much time looking at the implementation beyond 
integrating the libraries (as standalone libraries) into our build and 
packaging system. I have also spent some time looking at updating and 
building the documentation but otherwise not much time in the code itself.

However, for this review I took the time to browse the code and reason 
about what I was reading. From a cursory review the code is consistently 
structured, and mostly consistently formatted, and appears to follow 
good practices if perhaps a little terse for my liking.

That said, I could see myself being able to navigate the code easily 
enough in an effort to better understand, or find, or fix, an issue. I 
see from other reviews that some have indeed done this, and been able to 
reason sufficiently about the code to identify potential issues and make 
recommendations. I think that is worth calling out. Often library 
writers feel they are the only consumers of the code and so take that as 
a carte-blanche to write some cryptic and unintelligible code (yes I 
know in some cases there are good reasons but I am generalising here). I 
definitely don’t see an issue with that here.

Regarding 2: I’ll approach this from two angles. First I’ve built and 
executed tests using the libraries on numerous occasions. As part of a 
large migration over to a coroutine-first approach I’ve refactored some 
core libraries within our own codebase. These are foundational libraries 
that touch almost all of our codebase. This means almost every 
repository’s tests exercise this code and so indirectly exercise Capy 
and Corosio.

The primary focus of this testing was to understand and account for any 
behavioural differences, and then ensure that correctness was maintained 
by achieving passing tests. The outcome of this effort was that all 
tests across all components and repositories were executed and passed. 
This offered a strong indicator that the underlying implementation was 
of good quality and fit for purpose.

Second, I took our existing benchmark tests, which were designed to test 
“representative” production scenarios in a repeatable manner (as opposed 
to micro-benchmarks seeking to expose specific bottlenecks), and had 
them re-run using Capy/Corosio as the underlying implementation. Of 
course that meant our entire stack had to be converted from an Asio 
callback model to a coroutine-first Capy/Corosio model so there was 
plenty of scope to introduce issues, so this relied heavily on the 
ability to test against both our original codebase, and the updated one 
to confirm correctness as mentioned above.

To add some context these benchmarks are essentially run against a whole 
architecture pipeline which includes business logic and so on. As I 
said, “representative” of production flows. We tested 8 scenarios across 
both code paths (Asio and Capy/Corosio) using different message rates 
and configurations resulting in a significant set of results that 
represented a cross-section of use cases within our domain.

The encouraging outcome was that all the scenarios executed correctly 
and to completion, but more importantly they executed to a performance 
level that in general equalled, or surpassed, our base implementation. 
For me this represents quantitative evidence that the internal quality 
of the libraries is of a high standard and some effort has gone into 
ensuring a very high baseline for performance.

I think our success here speaks volumes for the quality of the libraries 
in the context of real-world use cases.

Aside: I know the authors of the libraries are very excited about the 
fact the libraries are not header-only, but really for me I see no major 
benefit in that. For us it just made it harder to integrate. It's 2026 
and if you are worried about compile times (still) then your code and 
development approach is probably your issue. We've 40+ repos, all 
header-only, with interdependencies and we don't get hung up on this. I 
know for some this might be a feature, but it wasn't for us.


4.  What is your evaluation of the documentation?
-------------------------------------------------

I’m going to approach this question from two perspectives: (a) structure 
and style, and (b) accuracy and completeness.

In consideration of structure and style I can say that in general I 
think the documentation is of very high quality and a “breath of fresh 
air”: no major assumptions assumed, code examples fully formed, and 
graduated and well thought out delivery of topics and features. I can 
see a great deal of effort and thought has gone into the documentation 
and there are too many good qualities to list them here.

I have a few nits though:

1. Capy: On the one hand the documentation for the examples is great. 
(a) the documentation actually exists, and (b) it walks through the 
whole of the examples. On the other hand I find it really irritating 
when I see “Exercises” at the end of an example. That’s lazy. If the 
exercise is worth doing then it is worth documenting. Moreover it is 
undoubtedly going to tell the reader something valuable so at the very 
least I’d expect some pointers to where you can find examples elsewhere 
in the docs that should provide enough pointers. I like that there was 
thought put into identifying canonical examples but let’s maximise their 
value if we can.

2. Both: The Reference needs much better cross-linking. I also feel like 
the Reference should be decomposed to display subheadings in the 
navigation bar. For example  Namespaces, Types, Type Aliases, Enums, 
Functions, Concepts - even if that just jumps to a section heading in 
the Reference page. When I’m doing a lot of dev I tend to spend a lot of 
time in the Reference and having to keep drilling down from a top entry 
to find what I am looking for and seeing types with no cross-links makes 
it hard to use. Ideally you’d almost have a separate Reference 
navigation tree/page but I can’t think how to make that work well.

3. Both: If you make a strong statement in the Introduction please make 
sure you reference and link to where in the documentation you actually 
explain that and why it matters. For example, in the Corosio 
introduction it says, "...that provides asynchronous networking 
primitives with automatic executor affinity propagation". Interesting 
for sure but given this is the opening sentence about why I should care 
I'd hoped to be able to read more about this and why I should care and 
what that means for me, the user. I see this as a missed opportunity.

In consideration of accuracy and completeness I can say it feels an 
awful lot like the Boost review arrived a bit quicker than the library 
authors anticipated and the documentation is not quite up-to-date. From 
the conversations in other reviews I can see that there are some 
accuracy issues that are known and will be fixed so I do not have any 
concerns here.

That said (and in reference to Capy in particular) I did find myself 
thinking a lot about lifetime and ownership. I couldn’t find an easy way 
to search the documentation (except by cloning the repo and physically 
searching the built local html pages) but it struck me that ideally 
there would be a section, probably under “Coroutines in Capy” called 
“Lifetime and Ownership”. Almost all bugs are going to come from either 
Lifetime and Ownership issues or synchronisation issues.

As someone who has spent years using callbacks in Asio where that’s (at 
least for me) really clear I wondered why I couldn’t just read a section 
that represented a synopsis of all the things I’d need to think about. 
In the end I created one (with some LLM help to scrape the rest of the 
docs) and then rebuilt the documentation to do this. I actually found it 
pretty helpful so I’m happy to share that with the authors in case they 
see value in adding something like that.

I would note also I had to figure out how to build the documentation 
since that’s not covered. I added it to the README of my local copy.

What I did with the section was cross reference to all other parts of 
the documentation that had relevant or specific documentation. The point 
was it helped me navigate the areas where this mattered while also 
helping me out with a high level view.

In summary then. The documentation is on the whole very, very good but 
without a lot more effort it could also be accurate and a little more 
complete.


5.  Have you used either or both libraries? What was your experience?
---------------------------------------------------------------------

Yes. I have used the libraries as part of an internal effort to see if 
we could port our codebase across to Capy and Corosio from an 
Asio-callback structure. The experience has been a very positive one, as 
mentioned elsewhere in this review.


6.  Are the libraries ready for inclusion in Boost?
---------------------------------------------------

Yes. Adding them now will ensure good evolution and adequate feedback 
from a broader group of people, inside a vehicle many developers are 
familiar with (Boost). I favour these making it into Boost sooner than 
later, because I believe they have a very strong foundational design but 
to really evolve effectively they need the kind of usage and challenge 
that we have been reading about in this review period.


7.  If not, what changes would you recommend before acceptance?
---------------------------------------------------------------

I believe they are ready for inclusion and any concerns I have raised 
are not blockers.


8.  Do the libraries fit well within the existing Boost ecosystem?
------------------------------------------------------------------

Yes - I think they fit well and I am sure over time interoperability 
with other related libraries will potentially increase in presence and 
value, as the libraries see wider use within the ecosystem and as users 
look to find low friction migration paths.

This is something we know about as we currently have a codebase that can 
work with either Boost.Asio or Capy/Corosio. from our experience we know 
it can be done (migrate or have dual implementations), but as more look 
to explore this I could see helpers or other interoperability features 
arise. Critically though, only if driven through usage which is as it 
should be, not on some theoretical need.


9.  Are there API, naming, usability, extensibility, or
     implementation concerns that should be addressed?
--------------------------------------------------------

When I started writing this review last week (yes I've been following 
the review conversations as I mull over my own thoughts) I was happy to 
say “no, none that I care strongly about”, however after seeing some 
comments in other threads I do think it is worth exploring whether or 
not IoAwaitable is the best name for IoAwaitable. I don’t have a strong 
preference for an alternative and could live with it as it is, but I do 
think some discussion could be helpful even if that just results in a 
more detailed rationale for why IoAwaitable is the right choice (for now).

Let me be clear though - I would have absolutely no problem with the 
name being changed (if it made sense) _after_ acceptance. On initial 
acceptance the user base will be small so I think breaking changes can 
be tolerated much more early in a library’s life. Not a condition of 
acceptance for me.


Closing Remarks
---------------

Boost is the right place for these libraries to grow. I take the points 
regarding some kind of Asio interoperability (as mentioned by another 
reviewer). Not sure what that would look like but I think if I 
understand the design it should be possible to add this later if the 
consensus was to do so?

On the note of Asio - I really do not see Capy/Corosio as a 
_replacement_ for Asio. I think this is a misfire in the representation 
of the role and value of these libraries. I see see Capy/Corosio as an 
_alternative_ to Asio that has a somewhat compatible (in terms of 
reasoning and design) execution model. Instead I think the libraries are 
an excellent showcase for the hard earned experience of Asio, repackaged 
into a leaner more focused form. That, combined with the library 
separation, makes these libraries potentially an easier vehicle on which 
to push for a more sensible model for standardisation.

Coming back to the libraries themselves - we have used these libraries 
extensively in real world settings as “end users” of the libraries. We 
have previously raised minor issues with the authors, or sought 
clarification on certain aspects of the design, but these have been 
addressed. We continue to explore how best to maximise our performance 
but we are already at “current performance” and so are satisfied with 
what we have so far.

I am sure we will field more questions and concerns as we continue in 
our use, but that’s the natural life of a library, and what we want is 
to also benefit from the input and concerns from others, which results 
in improvements we can all benefit from. This alone drives my desire to 
see these libraries in Boost.

Finally, given my only personal concerns for acceptance were 
documentation updates, which I see the authors are already committed to, 
I don’t feel the need to do a further review of that before acceptance. 
I trust it will happen, so:

+----------------------------------------------------+
| I vote for the libraries to be ACCEPTED into Boost |
+----------------------------------------------------+

Best wishes,
Jamie

_______________________________________________
Boost mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://lists.boost.org/mailman3/lists/boost.lists.boost.org/
Archived at: https://lists.boost.org/archives/list/[email protected]/message/PQW35UOWDPLV4YCZLT4W7JEQ5WRB6FEO/