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