Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use a stackful coroutine as the wait handler of a steady_timer which is defined inside the very stackful coroutine?

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

like image 769
updogliu Avatar asked Feb 14 '14 05:02

updogliu


2 Answers

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:

  • control exits the block specified by Work() via either a return, reaching end of function, or an exception unwinding the stack.
  • The associated 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.
like image 109
Tanner Sansbury Avatar answered Oct 14 '22 07:10

Tanner Sansbury


yes - it should work. boost::asio::steady_timer timer(io) registers the timer on the io-service.

like image 2
olk Avatar answered Oct 14 '22 07:10

olk