Can I use stackful coroutine and boost::asio::steady_timer::async_wait
in the following way?
The point is that (my understanding, not sure) during waiting, local variable timer
is not on the stack and thus inaccessible. So can the callback proceed normally? (FYI, it works fine on my Mac using clang++5.0 .)
boost::asio::io_service io;
void Work(boost::asio::yield_context yield) {
boost::asio::steady_timer timer(io);
timer.expires_from_now(std::chrono::seconds(5));
timer.async_wait(yield);
cout << "Woke up." << endl;
}
int main() {
boost::asio::spawn(io, Work);
io.run();
return 0;
}
I think it is worth a comparison between this question: boost asio deadline_timer
Yes, it is safe to pass boost::asio::yield_context
to an object that has automatic storage duration within the same coroutine.
Boost.Coroutine uses Boost.Context to perform context switching. Boost.Context provides a means to suspend the current execution path, preserving the stack (including local variables such as Work()
's timer
), and transfer execution control, allowing the same thread to run with a different stack. Hence, with the boost::asio::steady_timer
object having automatic storage duration, its lifespan will end when either:
Work()
via either a return
, reaching end of function, or an exception unwinding the stack.io_service
is destroyed. Internal handlers maintain shared ownership of the coroutine, and when the io_service
is destroyed, all associated handlers are also destroyed. This destruction will cause Boost.Coroutine to force each coroutine's stack to unwind.When boost::asio::spawn()
is invoked, Boost.Asio performs some setup work and then will dispatch() an internal handler that will create a coroutine using the user provided function as an entry point. When the yield_context
object is passed as a handler to asynchronous operations, Boost.Asio will yield immediately after initiating the asynchronous operation with a completion handler that will copy results and resume the coroutine. A strand
owned by the coroutine is used to guarantee the yield occurs before resume. Here is an attempt to illustrate the execution of the example code:
boost::asio::io_service io_service;
boost::asio::spawn(io_service, &Work);
`-- dispatch a coroutine creator
into the io_service.
io_service.run();
|-- invoke the coroutine creator
| handler.
| |-- create and jump into
| | into coroutine ----> Work()
: : |-- timer created
: : |-- setting timer expiration
: : |-- timer.async_wait(yield)
: : | |-- create error_code on stack
: : | |-- initiate async_wait operation,
: : | | passing in completion handler that
: : | | will resume the coroutine
| `-- return <---- | |-- yield
|-- io_service has work (the : :
| async_wait operation) : :
| ...async wait completes... : :
|-- invoke completion handler : :
| |-- copies error_code : :
| | provided by service : :
| | into the one on the : :
| | coroutine stack : :
| |-- resume ----> | `-- return error code
: : |-- cout << "Waked up." << endl;
: : |-- exiting Work() block, timer is
: : | destroyed.
| `-- return <---- `-- coroutine done, yielding
`-- no outstanding work in
io_service, return.
yes - it should work. boost::asio::steady_timer timer(io) registers the timer on the io-service.
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