Boost awaitable: how to await on async operation inside post
Asked Answered
W

1

6

I was writing some asio code and tried to refactor it to use C++20 coroutines. However I got stuck transforming this code:

asio::post(
    my_strand,
    [self = shared_from_this()]() {
        // functions that write in this container can only be called
        // on a single thread at a time, thus the strand
        session_write_history.push_back(buffer);
        /* co_await? */ socket.write_async(buffer, /* use awaitable? */);
    }
);

You see, my async operation is done inside the post callback, so using asio::use_awaitable on the async operation one would make the callback a coroutine. Is there a way to await on the async operation inside the asio::post on the strand?

Windstorm answered 16/2, 2021 at 14:36 Comment(0)
W
7

Simply send asio::use_awaitable instead of a callback to post. This will make your function awaitable. You will then be able to put your async calls directly in your function:

co_await asio::post(my_strand, asio::use_awaitable);
// ...
// code that runs on the strands context
// ...
co_await socket.async_write_some(buffer, asio::use_awaitable);

You can in fact, use asio::use_awaitable in any place where you would put a callback, so transforming callback async code into coroutines can be done pretty much 1:1

Windstorm answered 16/2, 2021 at 14:36 Comment(9)
Good analysis. It would be better if you clarified that the first sample line is a no-op and only there to show the consistency of the library interface?Disillusion
@Disillusion it is not a no-op. When running on multiple thread, this line ensure the rest of the function runs on the strand's context.Windstorm
Wouldn't it run on the socket's associated executor after the async_write_some anyways? (You're right about the immediate call to the initiating function for async_read_some though). It feels like there should be a better possibility (like associating the strand executor with the socket in the first place, binding the completion handler to the strand etc.). I'm not sure though - I didn't play with the awaitables much yet (Oh and the coawait this:coro::executor idiom seems relevant)Disillusion
@Disillusion in my actual code I have other actions that I must execute in a particular strand, independent of the execution context of the socket. I can add a note in my snippets.Windstorm
@GuillaumeRacicot thank you for this. How do you return then to the original (outer) execution context?Resupine
You might not be able to if the previous execution context wasn't in an executor to begin with. But if it was, you might just do co_await asio::post(previous_executor, asio::use_awaitable)Windstorm
@GuillaumeRacicot I've tried this approach and another. I can't get the result we want: gist.github.com/sketch34/9a3aa53887892b2181bc389003741118Resupine
@JarrodSmith You will have to ask another question for that. My problem was solved.Windstorm
@JarrodSmith I looked at your gist and I think my question may be about the same issue: https://mcmap.net/q/1355907/-asio-how-to-change-the-executor-inside-an-awaitable/4479969Strife

© 2022 - 2024 — McMap. All rights reserved.