I want to use the composed async operation asio::async_connect
, and cancel it, possibly between the individual calls to basic_socket::async_connect
which it makes.
In the same manner, I'd like to cancel my own composed async operations (which use async_compose
).
I am not asking how to cancel the non-composed member function basic_socket::async_connect
, for which I could call basic_socket::cancel
.
The free function is composed, so while calling cancel
on the socket might stop an ongoing socket::async_connect
, it would not stop queued operations.
How do I cancel asio::async_connect
?
In general, how do I cancel composed operations?
How do I cancel custom composed operations? (At least with this third option I could hack in some kind of cancellation token system that can check a flag between "building-block" async operations, but I still can't use existing composed operations in this case).
To summarize the problem, see this diagram from Robert Leahy's 2019 CppCon talk:
This shows the issue with async_write
, a composed operation which also has this problem.
Robert Leahy solves this by reimplementing async_write
in a wrapper object which checks a cancellation flag before calling every atomic async_write_some
operation on the underlying stream.
Perhaps this is the solution, but it means that I would have to re-implement every built-in composed asio operation to use this kind of similar wrapper object.
Revisiting my own question later for posterity: I think composed cancellation support is a pretty new feature and lots of this stuff is still experimental::
as of time of writing.
As of Asio 1.19.0, if a composed op supports cancellation (see docs for it), it appears you're able to bind a cancellation_slot
to it (with bind_cancellation_slot
) and trigger that with an associated cancellation_signal
, or by using logical operators with awaitables
which will trigger cancellation automatically.
This process is described here in the docs: https://think-async.com/Asio/boost_asio_1_22_1/doc/html/boost_asio/overview/core/cancellation.html with this example given:
class session
: public std::enable_shared_from_this<proxy>
{
...
void do_read()
{
auto self = shared_from_this();
socket_.async_read_some(
buffer(data_),
boost::asio::bind_cancellation_slot(
cancel_signal_.slot(),
[self](boost::system::error_code error, std::size_t n)
{
...
}
)
);
}
...
void request_cancel()
{
cancel_signal_.emit(boost::asio::cancellation_type::total);
}
...
boost::asio::cancellation_signal cancel_signal_;
};
These cancellation_slot
s/cancellation_signal
s appear to work like fancy std::stop_tokens
. Implementing cancellation with them in custom composed ops appears to be different depending on which method of composition you're using (async_compose
, experimental::co_composed
, callback chains), but the co_composed docs offers examples for manual and implicit cancellation checking for an echo protocol. co_composed
uses some magic auto state
object for cancellation checking though, which I'm guessing is a wrapper around a cancellation_slot
somewhere.