Skip to content

awaiting an experimental::promise with "cancel_after" doesn't post() completion on cancellation #1705

@pgit

Description

@pgit

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".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions