简体   繁体   中英

asio custom async function and c++20 coroutines

I'm migrating my asio application from its stackful coroutines to c++20 stackless coroutines. I have an existing class method that looks like this:

int my_async_op(asio::yield_context yield) {
    using tResult = asio::async_result<asio::yield_context, void(boost::system::error_code, int)>;
    tResult::completion_handler_type handler(yield);
    tResult result(handler);
    ...
    boost::system::error_code ec;
    asio::some_async_op(yield[ec]);
    ...
    handler(boost::system::error_code(), 42);
    return result.get();
}

...and is called like this:

boost::system::error_code ec;
x = my_async_op(yield[ec]);

When migrating to C++20 stackless coroutines, chaining is required and a skeleton of my function now looks something like this:

asio::awaitable<int> my_async_op(...) {
    ...
    boost::system::error_code ec;
    co_await asio::some_async_op(net::redirect_error(net::use_awaitable, ec));
    ...
    co_return 42;
}

...but is called like this:

boost::system::error_code ec;
x = co_await my_async_op(net::redirect_error(net::use_awaitable, ec));

So the skeleton needs updating to take a completion token, the same as native asio async ops, but I can find a reference example to work off of and I admit to finding asio source code difficult to parse.

Any guidance or references would be appreciated.

Edit: I think I'm getting close with asio::async_initiate per http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1943r0.html . My function now looks like this:

template<typename T>
auto my_async_op<T&& token) {
    return asio::async_initiate<T, void(boost::system::error_code, int)>(
        [&](auto handler)->void {
            ...
            boost::system::error_code ec;
            co_await asio::some_async_op(asio::redirect_error(asio::use_awaitable, ec));
            ...
            handler(boost::system::error_code(), 42);
        },
        token
    );
}

Only problem is that I get a compiler error on that nested co_await call:

XXX.h:97:12: error: unable to find the promise type for this coroutine
   97 | co_await asio::some_async_op(net::redirect_error(net::use_awaitable, ec));

Will keep grinding away.

Edit: Looking into this now... https://github.com/chriskohlhoff/asio/issues/795

If your requirement is to write your own async_xyz function you can use async_initiate. You should use another thread to run the function on.

I do this to compute a hash on a boost thread pool.

For example:

template <boost::asio::completion_token_for<void (std::string)> CompletionToken>
auto
async_hash (boost::asio::thread_pool &pool, boost::asio::io_context &io_context, std::string const &password, CompletionToken &&token)
{
  return boost::asio::async_initiate<CompletionToken, void (std::string)> (
      [&] (auto completion_handler, std::string const &passwordToHash) {
        auto io_eq = boost::asio::prefer (io_context.get_executor (), boost::asio::execution::outstanding_work.tracked);
        boost::asio::post (pool, [&, io_eq = std::move (io_eq), completion_handler = std::move (completion_handler), passwordToHash] () mutable {
          auto hashedPw = pw_to_hash (passwordToHash);
          boost::asio::post (io_eq, [hashedPw = std::move (hashedPw), completion_handler = std::move (completion_handler)] () mutable { completion_handler (hashedPw); });
        });
      },
      token, password);
}

And on the call side you can call it like the other async_xyz functions inside a coroutine:

auto hashedPw = co_await async_hash (pool, io_context, createAccountObject.password, boost::asio::use_awaitable);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM