Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to destroy boost::asio timer from its handler or handler dtor?

In our product we have code that can be simplified to essentially this function:

#include <boost/asio/steady_timer.hpp>
#include <functional>

void DelayedCall(
    boost::asio::io_service& io,
    boost::asio::steady_timer::duration delay,
    std::function<void()> f)
{
    auto timer = std::make_shared<boost::asio::steady_timer>(io, delay);
    timer->async_wait(
        [timer, f](boost::system::error_code const&)
        {
            // Probably it's ok to do even this:
            //timer.reset();
            f();
        }
    );
}

Assume that io object has global lifetime for simplicity.

As you can see timer object will be destroyed in handler's destructor.

When this question come to my mind, I was beware of some references in timer to executing handler and vise versa. I know that timer destructor will call cancel on all async waiting handlers, but this is irrelevant as handler already executing and can not be canceled.

Now I think that timer object simply post()s handler to io_service when time comes, so both timer and handler become independent from each other. Nevertheless quick attempt to investigate asio source code failed, so I'm still not certain that our code is correct.

I didn't find anything relevant in documentation neither here nor here.

like image 360
magras Avatar asked Sep 25 '22 08:09

magras


1 Answers

This is safe and well-defined for all I/O objects.


Boost.Asio has taken efforts to support using shared_ptr to extend the lifetime of an object, such as a steady_timer, to be at least as long as a sequence of asynchronous operations by binding a shared_ptr to the object into the handlers. The documentation notes:

[...] permits programs to simplify their resource management by using shared_ptr<>. Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), a shared_ptr to the object would be bound into the handlers for all asynchronous operations associated with it.

To support this, initiating functions accept handlers by-value, and Boost.Asio is responsibility for maintaining the validity of the handler. The implementation may make copies of the handler, and all copies are guaranteed to be destroyed:

  • immediately after the handler has been invoked
  • the io_service is destroyed
  • the io_service::service that owns the handler is shutdown via shutdown_service()

The requirements on asynchronous operations documentation states:

Arguments to initiating functions will be treated as follows:

— If the parameter is declared as a const reference or by-value, the program is not required to guarantee the validity of the argument after the initiating function completes. The implementation may make copies of the argument, and all copies will be destroyed no later than immediately after invocation of the handler.

like image 164
Tanner Sansbury Avatar answered Sep 28 '22 05:09

Tanner Sansbury