lewissbaker / cppcoro

A library of C++ coroutine abstractions for the coroutines TS
MIT License
3.41k stars 468 forks source link

boost::asio::awaitable<T> does not satisfy Awaiter<T> #131

Closed invexed closed 4 years ago

invexed commented 4 years ago
static_assert(cppcoro::is_awaitable_v<boost::asio::awaitable<void>>);

is being triggered, I presume due to boost::asio::awaitable<T>::await_suspend having the following signature:

template <class U>
void await_suspend(detail::coroutine_handle<detail::awaitable_frame<U, Executor>> h);

Is the Awaiter<T> concept over-constrained, or is this incompatibility the fault of ASIO? It would be great if it were compatible with cppcoro::when_all, for example.

lewissbaker commented 4 years ago

The design of asio's awaitable type is what I'd call "conditionally awaitable", which means that it's only awaitable in particular contexts.

The asio awaitable type is currently only awaitable within other asio awaitable coroutines.

The reason for this, I believe, is to support the ability to destroy an entire stack of coroutines from the root in the event that some an executor/async op decides to drop/destroy the completion handler without invoking it. To support this, the asio awaitable coroutine type needs to keep track of the top level coroutine so that when the leaf-level operation destroys the continuation handler that it can call .destroy() on the top level coroutine.

This requires cooperation between caller and callee to coordinate this kind of shutdown/ownership.

This approach is incompatible with the model that cppcoro adopts for coroutines, however, where cancellation is propagated by resuming from the leaf with a cancellation result and letting this propagate up towards the root rather than by destroying the stack from the root and letting the destruction propagate down towards the leaf.

The asio awaitable model integrates well with asio but is more limited. eg the asio model doesn't handle multiple concurrent child tasks, like those created with when_all(), since ownership of the coroutine stack/graph would be shared by all leaf operations and so we cannot just destroy the root coroutine when one of the leaf operations is dropped.

So, despite the name, asio awaitable doesn't actually satisfy the awaitable concept as far as cppcoro is concerned.

I hope that helps.

invexed commented 4 years ago

Thanks for such a comprehensive answer. That helps a lot.