How to wait for all buffers to complete: asio stackful coroutines?

I am running a series of coroutines with asio :: spawn, and I want to wait until they all finish, and then do another job. How can I do that?

The control flow is as follows:

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 a 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 result:

 // 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 expect:

 // 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 location of the line "main coro finishes")

+6
source share
2 answers

I found ... a workaround.

I can use a timer with infinite duration and undo it from the last sub-coroutine. This will start 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 do not like this solution because it abuses the notion of a timer and an object, and this is a possible waste of system resources.

+1
source

The best option would be to use fibers (boost.fiber integrates into boost.asio). boost :: fiber are coroutine + scheduler + synchronization classes (APIs such as std :: thread) and can be used in a boost.asio context such as coroutines.

0
source

Source: https://habr.com/ru/post/977020/


All Articles