It seems that promise::operator()(CompletionToken&&) doesn't handle cancellation properly, at least in some situations.
In the following example, two threads of execution are spawned. A promise is created from waiting on a timer that never expires. Then, this promise is awaited with a timeout
- When using a parallel group (via
operator||), cancellation seems to work.
- However, when using the
cancel_after completion token adapter, the coroutine is never continued.
The reason for this seems to be that in
the completion handler is invoked immediatelly on cancelation, instead of being scheduled for execution. When removing the
p->cancel() here, it works.
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <boost/asio/experimental/promise.hpp>
#include <boost/asio/experimental/use_promise.hpp>
#include <print>
using namespace boost::asio;
using namespace experimental;
using namespace awaitable_operators;
using namespace std::chrono_literals;
awaitable<void> cancel_promise(std::string variant)
{
auto ex = co_await this_coro::executor;
steady_timer forever(ex);
forever.expires_after(steady_timer::duration::max());
auto promise = forever.async_wait(use_promise);
std::println("{} awaiting promise...", variant);
steady_timer timer(ex);
// doesn't compile with Boost 1.90 because of missing executor_type / get_executor()
// co_await std::move(promise)(cancel_after(1ms));
if (variant == "cancel_after")
co_await std::move(promise)(as_tuple(cancel_after(timer, 1ms)));
else if (variant == "parallel group")
co_await (std::move(promise)(use_awaitable) || timer.async_wait(use_awaitable));
std::println("{} awaiting promise... STILL THERE", variant);
}
int main()
{
boost::asio::io_context context;
co_spawn(context, cancel_promise("cancel_after"), detached);
co_spawn(context, cancel_promise("parallel group"), detached);
context.run();
}
Expected output: "STILL THERE" for both "cancel_after" and "parallel group", but we get it only for "parallel group".
It seems that
promise::operator()(CompletionToken&&)doesn't handle cancellation properly, at least in some situations.In the following example, two threads of execution are spawned. A
promiseis created from waiting on a timer that never expires. Then, this promise is awaited with a timeoutoperator||), cancellation seems to work.cancel_aftercompletion token adapter, the coroutine is never continued.The reason for this seems to be that in
asio/include/asio/experimental/promise.hpp
Line 205 in 55684d4
p->cancel()here, it works.Expected output: "STILL THERE" for both "cancel_after" and "parallel group", but we get it only for "parallel group".