boost::asio::streambuf::consume - Injects garbage character

假装没事ソ 提交于 2019-12-01 12:42:17

The ý character indicates a debug heap memory fence:

This means that the streambuf pointers are corrupted.

I don't have the full picture for your program, but I do note that you're not properly synchronizing writes/reads on the socket.

The Send Side

For example, looking at MyClass::Send, it appends to the existing m_sendBuffer but sends all of it again using async_write. This is

  • redundant, because of anything was already in m_sendBuffer that would mean an async_write had already been started on it
  • undefined behaviour, because the docs say:

    This operation is implemented in terms of zero or more calls to the stream's async_write_some function, and is known as a composed operation. The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.

  • a data race: m_sendBuffer is being modified while a previous write operation could be flight

The usual approach to fixing this is to have a queue of outgoing buffers, instead of a single one, and send them in succession, e.g. boost asio async_write : how to not interleaving async_write calls?

The Receive Side

Here I don't actively spot any additional problems. The same problems from the send-side still apply though.

Once you have udefined behaviour, you can expect any behaviour, so also affecting the receive behaviour.

Most importantly, the read-loop causes a data-race to exist on m_socket: Send is triggered from the ProcessingThreadProc while the async_read loop is running on the service thread(s).

Thread Safety Of m_socket

The tcp::socket class is not thread-safe. Because you have a worker thread as well as the thread that runs the io_service (and hence the completion handlers) you cannot access m_socket without synchronization.

You should use a strand to serialize the operations on the shared object m_socket. In that case access to m_socket is already safe inside completion handlers running on that strand. All other accesses should be posted to the strand, like:

m_strand.post([this] {
    auto callback = boost::bind(&MyClass::OnSend, this, boost::asio::placeholders::error);
    async_write(m_socket, m_sendBuffer, m_strand.wrap(callback));
});

In fact, you could use the strand to synchronize access to the m_numPostedSocketIO variable, but that would remove full-duplex ability of read/write operations. Besides, you don't need the m_numPostedSocketIO counter:

Closing the socket

Note that closing the socket does already cancel all pending asynchronous IO operations. They will complete with the error code boost::asio::error::operation_aborted.

This means that you can simplify the part where you wait for pending IO operations to complete. You can simply close the socket. Notes:

  • if you have multiple service threads, have a strand to serialize operations
  • in that case to prevent unsynchronized access to m_socket, post on the strand:

    m_strand.post([this] { m_socket.close(); }); // e.g.
    

boost::asio::streambuf::consume overflows.

Use

buffer.consume(buffer.consume(buffer.size());

Rather than

buffer.consume(std::numeric_limits<size_t>::max());

Reporting bug to boost mailing list.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!