I've got simple io_context object which is used to initialize ssl_stream object (using async_resolve, async_connect and async_handshake).
On a different scope, it's used to call async_read and async_write to pass IO in that connection.
the async calls are performed from within coroutine (boost::asio::spawn(io_context_, [&](boost::asio::yield_context yield)). each one of the stages above is executed on a different such coroutine. In order to execute the coroutine, the underlying io_context of the ssl_stream should be in run() method.
However, since those 2 methods are separated, than a single run() stage wouldn't be enough. After the connection initialization will be finished, the first run() will be terminated, so a second run() instance should be called right after the second coroutine (that does the IO ops) is called)
However, I observed that the second run goes out immediately, and doesn't perform the recently inserted spawn. Any idea how to overcome this scenario ? or the only alternative is to run the whole connection lifecycle in single coroutine, or call the run on a separated thread that never quits...
Here's the semi-pseudo code of my scenario :
boost::asio::io_context io_context_;
std::optional<boost::beast::tcp_stream> stream_;
std::optional<boost::asio::ip::tcp::resolver> resolver_;
//STAGE 1
boost::asio::spawn(io_context_, [&](boost::asio::yield_context yield) {
results = resolver_->async_resolve(host_,port_ , yield);
ssl_stream_->next_layer().async_connect(results, yield);
ssl_stream_->async_handshake(ssl::stream_base::client, yield);
}
try {
io_context_.run();
} catch (...) {
}
//STAGE 2
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
boost::asio::spawn(io_context_, [&](boost::asio::yield_context yield) {
beast::get_lowest_layer(*ssl_stream_).expires_after(kOpTimeout);
auto sent = http::async_write(*ssl_stream_, beast_request, yield);
auto read = http::async_read(*ssl_stream_, buffer, res, yield);
});
try {
io_context_.run();
return res;
} catch (...) {
}
It is possible, but a-typical. As documented, in order to be able to re-run after the service ran out of work (I.e. returning 0 handlers when executed), you need to call
restart()
(previouslyreset()
):Demo
Here is a demo based on your snippet:
Prints, locally:
Notes
Again, make
buffer
a member, because its context will matter for any subsequent traffic on the connection.Also, don't use
run()
to find out when things are "done". You can use futures, or any other way of synchronization:Live On Coliru
Locally: