The traditional answer would have employed an awaitable timer (gera posted an answer around that).
However, that can be error prone and clumsy, especially with multithreading (both race conditions (e.g. https://stackoverflow.com/a/22204127/85371 and more specifically https://stackoverflow.com/a/43169596/85371) and data races (e.g. gera's code isn't threadsafe).
A lot more flexible is the experimental channels support: https://www.boost.org/doc/libs/master/doc/html/boost_asio/overview/channels.html
This will allow you to either use multiple channels for multiple events, or support many events over a single channel. Channels exist in thread-safe variant (asio::concurrent_channel
) out of the box.
Channel Demo
As per the comment request, here's a sample based on channels:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/experimental/concurrent_channel.hpp>
#include <iostream>
#include <memory>
namespace asio = boost::asio;
using boost::system::error_code;
using Channel = asio::experimental::concurrent_channel<void(error_code, std::size_t)>;
using CoChannel = asio::use_awaitable_t<>::as_default_on_t<Channel>;
asio::awaitable<void> myCoroutine(CoChannel& event) {
try {
std::cout << "Coroutine: waiting..." << std::endl;
while (true) {
auto evt = co_await event.async_receive();
std::cout << "Coroutine: resumed! (" << evt << ")" << std::endl;
}
} catch (boost::system::system_error const& e) {
std::cout << "Coroutine: " << e.code().message() << std::endl;
}
std::cout << "Coroutine: done" << std::endl;
}
int main() {
asio::thread_pool io;
CoChannel event(io);
co_spawn(io, myCoroutine(event), asio::detached);
for (auto i : {1, 2, 3}) {
std::cout << "Main: Sleeping for " << i << " seconds..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(i));
event.try_send(error_code{}, i);
; // Resume the coroutine
}
event.close();
io.join();
std::cout << "Main: done" << std::endl;
}
Testing locally (for visible timing):
