This is my review of the proposed Boost.Async:
> Does this library bring real benefit to C++ developers for real-world use cases?
Considering the current support for C++ coroutines in the standard and
the absence of networking support in the foreseeable future, this
library can serve real-world users for many years. I find the author's
choice to build on top of Asio to be a smart decision, as it enables
the effortless utilization of multiple other high-quality networking
libraries in the field.
The author has chosen a single-threaded design, and I must say I agree
with this approach for several reasons:
1. It leads to a simpler and more efficient implementation,
eliminating the need for mutexes and atomic operations for
synchronization.
2. Writing multithreaded asynchronous code is a complex task, and it
becomes even more challenging in coroutine functions due to their
ability to suspend in the middle.
3. Most of the time, the best way to leverage multithreading is by
offloading heavy computations to other threads, a possibility offered
by this design through concurrent channels (share memory by
communicating, not communicate by sharing memory).
4. Users can be confident that they won't encounter race conditions
during development while still being able to write concurrent code.
The library capitalizes on two fundamental optimization opportunities
in C++ coroutines that cannot be achieved in Asio:
1. It prevents unnecessary suspension and rescheduling through the use
of the `await_ready` customization point. This is particularly
valuable when developing high-level coroutine functions that
occasionally need to suspend but can often return results without
performing async operations. For example:
async::promise<Result> online_users() {
if (is_cached_)
co_return online_users_;
// asynchronously fetch online users
}
By utilizing `await_ready`, we can eliminate unnecessary rescheduling.
2. It utilizes symmetric-transfer to efficiently resume the next
coroutine without involving a scheduler. Asio requires resuming the
next coroutine handle on a scheduler to prevent stack exhaustion,
which results in significant indirection and branches. However, with
symmetric-transfer, this can be accomplished with a fraction of that
overhead.
> Do you have an application for this library?
I've used C++ coroutines in every Asio-based application I've worked
on in the last three years. The main issue I've encountered is the
lack of asynchronous synchronization primitives such as join, select,
wait_group, and condition variables, which I believe Boost.Async has
addressed to some extent.
> Does the API match current best practices?
I couldn't find a complete set of asynchronous synchronization
primitives, such as semaphores, barriers, condition variables,
mutexes, and futures/promises. While some of these primitives can be
simulated using channels, I couldn't find any examples of this in the
documentation.
> Is the documentation helpful and clear?
I believe the documentation is rather clear for those who already know
how to use Asio. I would recommend the author to start by explaining
how this library is related to Asio and what gaps it tries to fill.
Another issue I have encountered is the lack of inline documentation
in header files, which means users need to switch to their web browser
for even basic information that could have been accessible directly
within their IDE. Furthermore, the documentation lacks details
explaining how `interrupt_await` works and snippet examples for
`wait_group`, `with`, `select`, `gather`, `join`, and `thread`.
> Did you try to use it? What problems or surprises did you encounter?
I have only attempted to experiment with the provided examples and
didn't encounter any issues in the process.
> What is your evaluation of the implementation?
I have skimmed over the implementation, and I find the use of
`await_ready` and `symmetric-transfer` in coroutines to be effective.
I also like the idea of introducing the `interrupt_await` concept to
enable a lossless select.
I found the use of std::mt19937 in select and wait_group a bit
excessive; it might be possible to be replaced with a more lightweight
option like PCG or something similar with a smaller state.
> Are you knowledgeable about the subject?
I have some experience in working on asynchronous networking libraries
that conform to Asio's universal asynchronous model (psql and smpp)
and scheduler-aware synchronization primitives in Asio (future/promise
pairs and oneshot channel). I have some experience with using C++
coroutines in real-life applications.
> How much time did you spend evaluating the library?
I spent approximately 12 hours reading documentation and assessing the
implementation, and an additional 4 hours experimenting with the
provided examples.
> Please explicitly state that you either accept or reject the inclusion of this library into Boost.
I believe this is a useful library that can cover 90% of ASIO-based
applications with a much simpler and more intuitive interface. I
recommend conditional acceptance of Boost.Async into Boost, provided
the following issues are addressed:
1. Add the mentioned missing synchronization primitives or demonstrate
in the documentation how the same can be achieved using a channel.
2. Revise the documentation to cater to users who may have little
knowledge of ASIO.
I would like to mention that both Klemens and I are associated with
the C++ Alliance.
Thanks, Niall, for managing this review, and Klemens for submitting it.
Regards,
Mohammad