I have a function that returns boost::asio::awaitable. What is the idiomatic way to convert this awaitable to std::future?
I have a function that returns boost::asio::awaitable. What is the idiomatic way to convert this awaitable to std::future?
Before we get into the answer, be warned:
You should not, under any circumstance, get() or wait() a future to a boost::asio::awaitable from the same thread as the executor that is running the coroutine.
That being said.
That third parameter to co_spawn(), the one almost every example blindly sets to the magic detached constant? Its role is to tell boost::asio what to do once the coroutine has finished. detached simply means "do nothing". So the canonical way to fulfil a future from an awaitable<> should be via that mechanism.
Thankfully, asio already provides the use_future completion token. Pass that as the third parameter to co_spawn() and it will return a std::future<> of the matching return type.
boost::asio::awaitable<void> foo();
boost::asio::awaitable<int> bar();
std::future<void> foo_fut = boost::asio:co_spawn(io_context, foo(), boost::asio::use_future);
std::future<int> bar_fut = boost::asio:co_spawn(io_context, bar(), boost::asio::use_future);
Here's a full example:
#include <boost/asio/io_context.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_future.hpp>
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::io_context;
using boost::asio::use_future;
awaitable<void> foo() {
// Simulate foo taking a while to run
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "foo\n";
co_return;
}
int main() {
try {
io_context context;
std::future<void> fut = co_spawn(context, foo(), use_future);
std::thread waiter([fut=std::move(fut)](){
std::cout << "AAA\n";
fut.wait();
std::cout << "BBB\n";
});
context.run();
waiter.join();
} catch(const std::exception &ex) {
std::cerr << ex.what() << std::endl;
}
return 0;
}
Obviously, this means you need access to the io context in order to produce the future.
If you are already inside of a coroutine and have no access to the io_context, then you have to create the future before calling the subroutine, and co_await its result before fullfilling the future. std::promise<> is perfectly suited for that task:
awaitable<void> sub_foo() {
co_return;
}
awaitable<void> foo() {
std::promise<void> prom;
auto fut = prom.get_future();
// send the future somewhere
co_await sub_foo();
prom.set_value();
co_return;
}
If you are not within a coroutine, and do not have access to an io_context, then you can't invoke the coroutine in the first place. So either of these approaches should have you covered.