0

I am implementing something looks like a HTTP server, the design is: for a already established connection, I want to reuse it for several requests, so I start another reading task with async_read on it when a request is finished, and also start a deadline_timer. If there is no input in 60 seconds, the timer will be triggered and the connection will be destructed. The thing that annoys me is that before the invocation of the connection's destructor, the callback we set to async_read will be invoked.

So, my question is, is there any way to cancel the pending reading task, that is, destruct the connection without the callback function invoked?

If the generic description above is not clear, the detail work flow is as below(code is attached at the bottom):

  1. cleanup() is called when a request finished;
  2. start the timer and another reading task in cleanup();
  3. if time is out, HandleTimeout() is called, and it calls stop();
  4. in stop(), do the clean work, and after it, the connection instance will be destructed.

but, after step 4, the callback() function will be called, which is registered in AsyncRead(), so, is there any way to cancel the invocation of callback()?

code:

class Connection : public boost::enable_shared_from_this<Connection>,
                   private boost::noncopyable {
public:
    typedef Connection this_type;

    void cleanup() {
        timer_.expires_from_now(boost::posix_time::seconds(kDefaultTimeout));
        timer_.async_wait(boost::bind(&this_type::HandleTimeout,
                                      shared_from_this(),
                                      boost::asio::placeholders::error));
        AsyncRead();
    }

    void AsyncRead() {
        boost::asio::async_read(*socket_, in_, boost::asio::transfer_at_least(1),
                                boost::bind(&this_type::callback,
                                            shared_from_this(),
                                            boost::asio::placeholders::error));
    }

    void callback(const boost::system::error_code& e) {
        // ...
    }

    void HandleTimeout(const boost::system::error_code& e) {
        if(e == boost::asio::error::operation_aborted)
            LDEBUG << "The timeout timer is cancelled.";
        else if(e)
            LERROR << "Error occurred with the timer, message: " << e.message();
        else if(timer_.expires_at()
                   <= boost::asio::deadline_timer::traits_type::now()) {
            LDEBUG << "Connection timed out, close it.";
            stop();
        }
    }

    virtual void stop() {
        connected_ = false;
        socket_->close();

        connection_manager_.stop(shared_from_this());
    }

private:
    // ...
    boost::asio::deadline_timer timer_;
};
Kelvin Hu
  • 1,299
  • 16
  • 31

1 Answers1

1

There is no clean way to accomplish this. The only way to guarantee that ready-to-run handlers, such as Connection::callback(), will not be invoked is to either:

  • Stop processing the io_service event loop.
  • Destroy the io_service, as the io_service's destructor will cause all outstanding handlers to be destroyed.

In the example code, consider returning in Connection::callback() if the socket is no longer open:

void callback(const boost::system::error_code& error)
{
  if (!socket_.is_open()) return;
  // ...
}

Also note that the error_code argument is not enough to deduce whether the timeout has occurred. It is possible that Connection::callback() is queued for invocation with an error_code of boost::system::errc::success when socket::close() is invoked. Hence, there are no operations to cancel.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Thanks for your answer, I just want to confirm if there is a way to do this, now the answer seems to be "No", yeah, I need a workaround to do this. – Kelvin Hu Dec 24 '13 at 05:34
  • @KelvinHu Can you elaborate on why you _need_ a different behavior? If it only _annoys_ you that `callback()` is invoked before `Connection`'s destruction, then consider decoupling the lifetime of `Connection` from the callback handler via `boost::weak_ptr`. This [answer](http://stackoverflow.com/a/18110168/1053968) demonstrates a basic weak pointer binder, and this [answer](http://stackoverflow.com/a/19622084/1053968) explicitly passes `weak_ptr` to a handler. In either solution, a handler _will_ be invoked, but `callback()` will only be invoked if `Connection` is alive. – Tanner Sansbury Dec 24 '13 at 15:25