1

I am using Boost Asio Library. I am facing difficulty while using boost::async_read() operation. I am using like this. The sample code for server asio read operation is given below.

void HandleClient::StartHandling()
{
    auto self(shared_from_this());
    asio::async_read_until(m_Sock,
                           m_Request,
                           DELIMITER,
                           [this, self](
                           const boost::system::error_code& a_Ec,
                           std::size_t a_BytesRead)
    {
        onRequestReceived(a_Ec, a_BytesRead);
    });
}

void HandleClient::onRequestReceived(const system::error_code &a_Ec, 
        std::size_t a_BytesRead)
{
    if(a_Ec)
    {....}

    auto self(shared_from_this());
    if(a_BytesRead > 0)
    {
    
        auto response = ProcessData(m_Request);
        if(!response.empty())
        {
            m_Data = response + DELIMITER;
            asio::async_write(m_Sock, asio::buffer(m_Data),
                [this, self](boost::system::error_code& a_Ec, std::size_t 
                a_Bytestransferred))
        {
             response_sent(a_Ec, a_Bytestransferred);
        });
    }


    asio::async_read_until(
        m_Sock,
        m_Request,
        DELIMITER,
        [this, self](const boost::system::error_code& a_Ec, std::size_t 
     a_BytesRead)
    {
        onRequestReceived(a_Ec, a_BytesRead);
    });
}

It is getting crashed after some iteration. The crash is random. Client side code is in form of synchronous call. I hope it is not issue. The error message I am getting debug assertion failed message:

Debug assertion failed Program:xx/vc/tools/msvc/14.16.27023/include/xstring Line 1427. Expression cannot deference string iteration because the iterator was invalidated (e.g. reallocation occurred, or the string was destroyed)

lalit gangwar
  • 389
  • 4
  • 10
  • Please copy and paste instead of retyping in your question. As given the code has omissions and blatant typos, and so does the error message ("iteratot"?). – sehe Aug 12 '20 at 20:43
  • Hey Sehe, sorry for the typo.I have updated the question for more clarity. I selected to write as I was afraid I might slip extra information. – lalit gangwar Aug 13 '20 at 04:03

2 Answers2

2

We don't know what the lifetimes of things are.

If we assume that m_Data is a std::string which is a member of the class that uses std::enable_shared_from_this then modulo the obvious typo:

auto self(shared_from_this));

it looks like things should be ok in terms of lifetime. However, since you do NOT appear to synchronize anything in terms of the reponse buffer (m_Data?) it looks like it is conceivable that the next onRequestReceived ends up calling this same code again, potentially overwriting m_Data with the new response, before the previous response was completely written.

It's very hard to guess this from the code shown as e.g. response and m_Data do not appear to have any explicit relation in it:

auto response = ProcessData(m_Request);
asio::async_write(m_Sock, asio::buffer(m_Data),
      // where did `m_Data` come
      // from?!

      // response **looks** like a local variable which
      // is dangerous in async code (of course it might
      // be a [smart] pointer or something, but we can't
      // tell

Summarizing the observations:

  • consider a logical strand doing the request read only AFTER completion of the response (e.g. when you do response_sent).
  • consider a strand to serialize all operations for this class (likely a connection? session?)

See also: Why do I need strand per connection when using boost::asio?

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Hey @Sehe, First of all thanks for answering. I have updated the sample code which might clear some doubt. Yes m_Data is std::string. You are right that this code is being called again and again. I would try implement it with strand and please take a look at updated code. – lalit gangwar Aug 13 '20 at 04:08
  • With the additions, the same observations still stand. I also note that `ProcessData` does not take into account `a_BytesRead`, This is likely an error, because more data may be in the buffer (`m_Request`) beyond the delimiter. See e.g. https://stackoverflow.com/a/49759113/85371 or https://stackoverflow.com/a/46912700/85371 or https://stackoverflow.com/a/33136051/85371 – sehe Aug 13 '20 at 12:12
0

@Sehe pointed correctly that buffer is getting overwritten because of next async call before previous one gets complete. The debugging assertion occurs generally when buffer lifetime has problem. It is programmer responsibility to keep the buffer in scope till async call gets completed. the corrected code for my question is:

void Connection::onRequestReceived(
    const system::error_code &a_Ec, 
    std::size_t a_BytesRead)
{
auto self(shared_from_this());
if (a_Ec)
{
    putErrorLog(getClientInfo(), a_Ec.value(), a_Ec.message(), __FUNCTION__, __LINE__);

    // Close on Client disconnect, Need to handle errors
    onFinish();
    return;
}

// Dispatch the request
if (a_BytesRead > 0)
{
    // Process received message
    auto response = ProcessRequest(m_Request);

    // if the response is not an empty string,
    // send it back to the client as a response
    if (!response.empty())
    {
        m_Data = response + DELIMITER;

        asio::async_write(
            *m_Sock.get(),
            asio::buffer(m_Data , m_Data .size()),
            m_Strand.wrap( [this, self](
                const boost::system::error_code& a_Ec,
                std::size_t a_BytesTransferred)
        {
            onResponseSent(a_Ec, a_BytesTransferred);
        }));
    }
}
else
{
    auto self(shared_from_this());
    asio::async_read_until(
        *m_Sock.get(),
        m_Request,
        DELIMITER,
        m_Strand.wrap([this, self](const 
boost::system::error_code& a_Ec, std::size_t a_BytesRead)
            {
                onRequestReceived(a_Ec, a_BytesRead);
            }));
}

}

void Connection::onResponseSent(const system::error_code &a_Ec, std::size_t 
a_BytesTransferred)
{

if (a_Ec) 
{
    putErrorLog(getClientInfo(), a_Ec.value(), a_Ec.message(), __FUNCTION__, 
__LINE__);
}
else
{
    auto self(shared_from_this());
    
    asio::async_read_until(
        *m_Sock.get(),
        m_Request,
        DELIMITER,
        m_Strand.wrap([this, self](const boost::system::error_code& 
a_Ec, std::size_t a_BytesRead)
            {
                onRequestReceived(a_Ec, a_BytesRead);
            }));
}
}
lalit gangwar
  • 389
  • 4
  • 10