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
#include
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());
}
}