[asio][coroutine] Intentional synchronization of a single coroutine
Within a large asynchronous project, I have a coroutine that needs to synchronize with other calls to that coroutine. This is a very simplified (and somewhat silly) example: #include <iostream> #include <cstdlib> #include <chrono> #include <boost/asio.hpp> #include <boost/asio/spawn.hpp> using namespace std; using namespace std::chrono; using namespace std::placeholders; using namespace boost::asio; using namespace std::chrono_literals; io_service ioservice; void bottleneck(seconds duration, yield_context yield) { steady_timer delay_timer(ioservice); delay_timer.expires_after(duration); delay_timer.async_wait(yield); std::cout << duration.count() << " "; } int main() { spawn(ioservice, std::bind(&bottleneck, 3s, _1)); spawn(ioservice, std::bind(&bottleneck, 2s, _1)); spawn(ioservice, std::bind(&bottleneck, 1s, _1)); spawn(ioservice, std::bind(&bottleneck, 0s, _1)); ioservice.run(); } The output is, of course, 0 1 2 3 because the timer effectively acts to re-order the calls. What I need is a way for each call to bottleneck() to fully complete before the next one starts, and each call occurs in the order they were called. This would make the output 3 2 1 0. I also want to allow other asynchronous routines on the io_service to run when the coroutine is yielding (so no cheating and turning the delay timer into a sleep). I've tried to think through something like the following. But I have no idea how to make it work. yield_context_queue bottleneck_queue; void bottleneck(seconds duration, yield_context yield) { bool yield_time = !bottleneck_queue.empty() bottleneck_queue.push_back(yield); if( yield_time ) { yield_back; } steady_timer delay_timer(ioservice); delay_timer.expires_after(duration); delay_timer.async_wait(yield); std::cout << duration.count() << " "; bottleneck_queue.pop(); if( false == bottleneck_queue.empty() ) { ioservice.post(bottleneck_queue.peak()); } }
Em sex, 9 de ago de 2019 às 16:12, Jared McIntyre via Boost < boost@lists.boost.org> escreveu:
What I need is a way for each call to bottleneck() to fully complete before the next one starts, and each call occurs in the order they were called. This would make the output 3 2 1 0. I also want to allow other asynchronous routines on the io_service to run when the coroutine is yielding (so no cheating and turning the delay timer into a sleep).
Take a look at this project: https://github.com/blinktrade/iofiber It should solve your needs. You could... like: spawn(ioservice, [](fiber::this_fiber this_fiber) { spawn(this_fiber, std::bind(&bottleneck, 3s, _1)) .join(this_fiber); spawn(this_fiber, std::bind(&bottleneck, 2s, _1)) .join(this_fiber); spawn(this_fiber, std::bind(&bottleneck, 1s, _1)) .join(this_fiber); spawn(this_fiber, std::bind(&bottleneck, 0s, _1)) .join(this_fiber); }) .detach(); With IOFiber, spawn() returns a fiber handle that you can use to join() the fiber. The handle is moveable and then you can pass it around. Check the documentation. -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
participants (3)
-
Bjorn Reese
-
Jared McIntyre
-
Vinícius dos Santos Oliveira