2

Boost Version : 1.68

C++ Standard : C++17

Development Platform : MSVC 2017

Operating System : Windows 10 Professional

PC Architecture : x64

I am using Boost::Asio to create an asynchronous TCP connection. During first successful connection everything works properly. Due to certain problem it socket break it attempts to re-connect and that time I get the run-time error. Even though I get run-time error the program still is able to receive data.

At first I was attempting to reconnect the socket in my main function's while (infinite) loop (main thread), but I was getting the error. I was getting error in

D:\vcpkg\installed\x64-windows\include\boost\asio\detail\impl\win_iocp_io_context.ipp

size_t win_iocp_io_context::run(boost::system::error_code& ec)
{
  if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  {
    stop();
    ec = boost::system::error_code();
    return 0;
  }

  win_iocp_thread_info this_thread;
  thread_call_stack::context ctx(this, this_thread);

  size_t n = 0;
  while (do_one(INFINITE, ec))
    if (n != (std::numeric_limits<size_t>::max)())
      ++n;
  return n;
}

on line when n = 13

while (do_one(INFINITE, ec))

I solved the error by adding my connect call in my receive and send handler once they detected connection is broken or socket is down.

Can someone explain why I faced the issue when attempting to reconnect from main thread and the issue got resolved when I tried to reconnect immediately after socket was broken in io_context thread.

I did restart context before I called run after connection was lost and io_context thread exited.

While Loop in my main function :

while (true)
{
    fmt::print("Socket Alive : {}\n", as.isSocketAlive());
    while(not as.isSocketAlive() and not as.isConnectionInProcess())
        as.connectSocket();
    if (not as.isSocketAlive())
        continue;
    if (as.isReadComplete())
        as.sendDataSocket(as.getRecievedData());
}   

My Async-Socket Functions :

void AsyncSocket::runContext(void)
{
    std::atomic_store(std::addressof(this->contextExitted), false);
    this->context.run();
    std::atomic_store(std::addressof(this->contextExitted), true);
}

void AsyncSocket::restartContext(void)
{
    if (std::atomic_load(std::addressof(this->contextExitted)))
        this->context.restart();
}

void AsyncSocket::connectSocket(void)
{
    std::atomic_store(std::addressof(this->socketAlive), false);
    std::atomic_store(std::addressof(this->connectionInProcess), true);
    //this->socket.async_connect(this->endpoint, std::bind(&AsyncSocket::connectHandler, this, boost::asio::placeholders::error));
    this->socket.async_connect(this->endpoint, boost::bind(&AsyncSocket::connectHandler, this, boost::asio::placeholders::error));
    //this->socket.connect(this->endpoint, );
    if (this->context.stopped())
    {
        if (this->isContextExitted())
            this->restartContext();
        this->t = std::thread(&AsyncSocket::runContext, this);
    }
    std::call_once(this->firstRun, [this]() {
        t = std::thread(&AsyncSocket::runContext, this);
    });
}

void AsyncSocket::recieveHandler(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
    fmt::print("In recieve handler, Error Code : {}\nBytes recieved : {}\n", ec.message(), bytes_transferred);
    try
    {
        std::atomic_store(std::addressof(this->receivingData), false);
        if (ec not_eq boost::system::errc::success)
        {
#ifdef _DEBUG
            fmt::print("Error in WebSocket::recieveHandler. Error : {0}\n", ec.message());
#endif // _DEBUG
            LOG_ERROR << ec.message();
            switch (ec.value())
            {
            case boost::asio::error::eof :
            case boost::asio::error::connection_reset :
            case boost::asio::error::connection_aborted :
            case boost::asio::error::network_reset :
            case boost::asio::error::network_down :
            case boost::asio::error::network_unreachable :
                if (this->isSocketAlive() and this->socket.is_open())
                    this->socket.close();
                std::atomic_store(std::addressof(this->socketAlive), false);
                this->connectSocket();// If I comment this line and try to reconnect in my main function (infinite while loop), I get mentioned run-time error
                return;
            default:
                break;
            }

        }
        else
        {
            this->readDataQueue.push(std::string(reinterpret_cast <const char *> (this->readDataBuffer.data()), bytes_transferred));
            std::atomic_store(std::addressof(this->readComplete), true);
        }
    }
    catch (const std::exception& ex)
    {
#ifdef _DEBUG
        fmt::print("Error in WebSocket::sendHandler. Error : {0}\n", ex.what());
#endif // _DEBUG
        LOG_ERROR << "Exception : " << ex.what() << "Data : " << this->writeDataQueue.front();
    }
    this->recieveDataSocket();
}



void AsyncSocket::sendHandler(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
    fmt::print("In send handler, Error Code : {}\nBytes recieved : {}\n", ec.message(), bytes_transferred);
    try
    {
        if (ec not_eq boost::system::errc::success)
        {
#ifdef _DEBUG
            fmt::print("Error in WebSocket::recieveHandler. Error : {0}\n", ec.message());
#endif // _DEBUG
            LOG_ERROR << ec.message();
            switch (ec.value())
            {
            case boost::asio::error::eof:
            case boost::asio::error::connection_reset:
            case boost::asio::error::connection_aborted:
            case boost::asio::error::network_reset:
            case boost::asio::error::network_down:
            case boost::asio::error::network_unreachable:
                if (this->isSocketAlive() and this->socket.is_open())
                    this->socket.close();
                std::atomic_store(std::addressof(this->socketAlive), false);
                this->connectSocket();// If I comment this line and try to reconnect in my main function (infinite while loop), I get mentioned run-time error
                return;
            default:
                break;
            }

        }
        this->writeDataQueue.pop();
        std::atomic_init(std::addressof(this->sendingData), false);
        this->writeSocket();
    }
    catch (const std::exception& ex)
    {
#ifdef _DEBUG
        fmt::print("Error in WebSocket::sendHandler. Error : {0}\n", ex.what());
#endif // _DEBUG
        LOG_ERROR << "Exception : " << ex.what() << "Data : " << this->writeDataQueue.front();
    }
}

Run-time Error

Dark Sorrow
  • 1,681
  • 14
  • 37

1 Answers1

1

abort is called as default action by std::terminate. terminate is called when a thread is destroyed or is overwritten by operator=(thread&&) and it is in joinable state.

This snippet of code is not-safe:

connectSocket() {
...
if (this->context.stopped())
{
    if (this->isContextExitted())
        this->restartContext();
    this->t = std::thread(&AsyncSocket::runContext, this); // [1]
}
std::call_once(this->firstRun, [this]() {
    t = std::thread(&AsyncSocket::runContext, this); // [2]
});

at first call of connectSocket line [2] is executed and thread started. At N-th call of connectSocket if context.stopped() returns true, you are creating new thread and assign it to t - thread which is joinable state, at this moment terminate is called. Before move assignment you should join t thread.

rafix07
  • 20,001
  • 3
  • 20
  • 33