1

I wrote an asynchronous SSL socket implementation using standalone asio and am struggling to get it to reconnect after a connection reset / close by the server. I am rather new to the asio library so please bear with me.

The thread that calls io_context::run remains blocked even after a disconnect because of the steady_timer. My close() logic is responsible for resetting the socket resources and is also responsible for trying to kill the timer. This is what my code looks like right now:

Creating my async job:

timer.async_wait(std::bind(&ssl_socket::heartbeat, this));

In my close() method:

timer.expires_at(std::chrono::steady_clock::now());
timer.cancel();

According to the boost docs, cancel() should:

Cancel any asynchronous operations that are waiting on the timer.

Perhaps I misinterpreting this but I would imagine this also cancels the asynchronous job that is bound to the io_context but it doesn't. io_context::run is never released and creates a deadlock.

This is what my timer handler looks like:

void ssl_socket::heartbeat() {
    spdlog::get("console")->trace("heartbeat called");

    if (connected_) {
        write(heartbeat_token);
        spdlog::get("console")->trace("heartbeat sent");
    }

    timer.expires_at(std::chrono::steady_clock::now() + std::chrono::seconds(heartbeat_interval));
    timer.async_wait(std::bind(&ssl_socket::heartbeat, this));

}

I would like to keep handler away from having to validate if it should renew its timer and let the close() deal with that (if possible).

user0000001
  • 2,092
  • 2
  • 20
  • 48

1 Answers1

1

You are ignoring the error code.

According to the boost docs, cancel() should:

Cancel any asynchronous operations that are waiting on the timer.

This is a bit misleading. When you read the full description for the cancel function you'll see:

This function forces the completion of any pending asynchronous wait operations against the timer. The handler for each cancelled operation will be invoked with the boost::asio::error::operation_aborted error code.

Which means, your handler will be called by the cancel function, and since your handler just re-sets the expiry-time and waits again, the cycle never ends. You need to check the error code and just break out of the cycle if it is set.

if(error) return;
tkausl
  • 13,686
  • 2
  • 33
  • 50
  • I have applied the update on my end but receiving a build error, as if `async_wait()` does not expect any parameters. I looked at [another stackoverflow post](https://stackoverflow.com/questions/33044461/boost-async-wait-and-adding-this-in-a-bind) and they also aren't expecting an `error_code`. Very confusing. – user0000001 Apr 21 '19 at 17:33
  • You might need to pass `std::placeholders::_1` as third parameter to `std::bind` – tkausl Apr 21 '19 at 17:37
  • I tried that, received build errors. Removed it, received build errors. I am going to update OP to reflect what I have now. – user0000001 Apr 21 '19 at 17:37
  • I had to update my renewal to support the addition of a parameter as well, hence the build errors. Looks like it has built! Trying this now. – user0000001 Apr 21 '19 at 17:50