C++-20 with coroutines (co_await), with boost::asio. I'm implementing a producer/consumer relationship: the consumer runs in a co-routine, the producer fires from any other thread. Using std::queue for transport.
If it were reversed with coroutine as producer, it's straightforward: my coroutine would do a unique_lock, push into the queue, release the lock, then call the data_ready_condition.notify_one(). Threads would use data_ready_condition.wait() with the mutex, pop() from the queue with the mutex locked on the way out of wait(), unlock the mutex, and be on our way. Classic thread synchronization.
Back to the original problem: the threaded producer does the classic lock/push/unlock, but now the thread would do a data_ready_condition.notify_one() ... but there seems to be no such async version of std::condition_variable for the coroutine side, that would do this:
co_await data_ready_condition.async_wait(); // No such thing?
I can't find it, or anything truly equivalent, in the asio docs. I thought I was being clever using asio::steady_timer and using cancel() in place of notify_one(). But now I don't think this will cut it without the mutex really coordinated with it.
So is there a way to do a true async_wait() with either std::condition_variable or something that asio provides? (It seems like io_context would supply this bridge somehow, since that's related to its job, but again I can't find it.)
Any other std:: or 3rd-party library I should know about? The compiler itself provides co_await, right? But I don't see anything outside of asio designed to harness it.
What am I missing?
I'm using boost::asio 1.83, gcc version 13.2.0 (Rev2, Built by MSYS2 project), -std=c++20 -fcoroutines
std::queueis not a transport. It's a medium.I think I can, see below
Yes, this has been established pattern for about a (half) decade of Asio. Note that there is is even
cancel_onethese days, so you can more usefully model condition variables.Somewhat true. You can get around the need for explicit synchronization (locking) by using strands ("Strands: Use Threads Without Explicit Locking"). However, if you really want to use non-asio threads you have no other option than to use tradional thread synchronization.
Channels
Without further ado, let me suggest Channels.
In your case you could use a
concurrent_channeland consume it from many coroutines.Example
With a blocking (synchronous) producer thread:
Live On Coliru
Typical outputs:
BONUS: Async Producer
Note that synchronous producers may result in sub-optimal throughput (Boost Asio experimental channel poor performance). Also, I think async producers are much more elegant:
Live On Coliru
With very similar outputs: