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).

1 Answers

1
tkausl On Best Solutions

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;