I'm starting a number of coroutines with asio::spawn, and I want to wait until all of then are finished and do some other work then. How it can be done?
The control flow is following:
asio::spawn (io, [] (asio::yield_context yield) {
...
// starting few coroutines
asio::spawn (yield, [] (asio::yield_context yield2) { ... });
asio::spawn (yield, [] (asio::yield_context yield2) { ... });
asio::spawn (yield, [] (asio::yield_context yield2) { ... });
asio::spawn (yield, [] (asio::yield_context yield2) { ... });
// now I want to wait for all of them to finish before I do
// some other work?
...
});
io.run ();
UPDATE
Below is the sample code
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <iostream>
using namespace std;
int main ()
{
using namespace boost::asio;
io_service io;
spawn (io, [&] (yield_context yield) {
cout << "main coro starts\n";
auto lambda = [&] (yield_context yield)
{
cout << "in lambda inside subcoroutine - starts\n";
steady_timer t (io, std::chrono::seconds (1));
t.async_wait (yield);
cout << "in lambda inside subcoroutine - finishes\n";
};
// starting few coroutines
spawn (yield, lambda);
spawn (yield, lambda);
// now I want to wait for all of them to finish before I do
// some other work?
// ???
cout << "main coro finishes\n";
});
io.run ();
}
And the output is:
// main coro starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - starts
// main coro finishes <----
// in lambda inside subcoroutine - finishes
// in lambda inside subcoroutine - finishes
While I'm expecting:
// main coro starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - finishes
// in lambda inside subcoroutine - finishes
// main coro finishes
(see the place of "main coro finishes" line)
I found a ... sort of workaround.
I can use a timer with infinite duration, and cancel it from last sub-coroutine. This will wake up the main coroutine.
Coliru Example
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <iostream>
using namespace std;
int main ()
{
using namespace boost::asio;
io_service io;
spawn (io, [&] (yield_context yield) {
cout << "main coro starts\n";
steady_timer rendez_vous (io, steady_timer::clock_type::duration::max ());
/* volatile */ int counter = 2;
auto lambda = [&] (yield_context yield)
{
cout << "in lambda inside subcoroutine - starts\n";
steady_timer t (io, boost::chrono::seconds (1));
t.async_wait (yield);
cout << "in lambda inside subcoroutine - finishes\n";
if (--counter == 0)
rendez_vous.cancel ();
};
// starting few coroutines
spawn (yield, lambda);
spawn (yield, lambda);
// now I want to wait for all of them to finish before I do
// some other work?
// ???
boost::system::error_code ignored_ec;
rendez_vous.async_wait (yield [ignored_ec]);
// ignore errors here by reason.
cout << "main coro finishes\n";
});
io.run ();
}
Frankly I don't like this solution, because it abuses "timer" concept and object and it is a possible waste of system resources.
A better option would be using fibers (boost.fiber integrates into boost.asio). a boost::fiber is a coroutine + scheduler + synchronization classes (API like std::thread) and can be used in the boost.asio context like coroutines.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With