1

Im using boost asio to do my networking and i noticed that when i switched to ssl my networking code doesnt work anymore. The problem seems to be this line here:

boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
        boost::bind(&connection::handle_read_header<Handler>,
            this, boost::asio::placeholders::error,
            boost::make_tuple(handler)));

Now as far as i understand it this should always read inbound_header_.size() bytes before calling the handler. And it works most of the time. But sometimes it will only read 0 bytes and still call the handler with error code 0. Is there anything im doing wrong? I copied that code from the boost asio serialization example so i thought this should work.

Minimal working sample here Tested with boost 1.61.0 on Ubuntu 16.04 Assert in connection::handle_read_header is hit in the first few seconds after launch nearly every time.

ACB
  • 1,607
  • 11
  • 31
  • Could a [mcve] please be provided? There could be multiple causes (e.g. `inbound_header_.size()` is `0`; failure to meet thread-safety requirement for `socket_`; lower-level drivers violating API contracts; etc). – Tanner Sansbury Dec 22 '16 at 15:02
  • added one to starting post – ACB Dec 22 '16 at 18:04
  • For posterity, the [mcve] should be minimal, often created from scratch, and contained within the original question. External links, and critical information they may contain, may rot away. – Tanner Sansbury Dec 22 '16 at 18:35

1 Answers1

1

async_read() will read until either the buffers are full or an error occurs. If the read handler is being invoked with neither of these conditions being satisfied, then one potential culprit is that undefined behavior has been invoked. In this particular case, the code violates the thread safety requirement for boost::asio::ssl:::stream:

Distinct objects: Safe.

Shared objects: Unsafe. The application must also ensure that all asynchronous operations are performed within the same implicit or explicit strand.

Specifically, the write operations are performed within an explicit strand (connection::strand_); however, the read operations are performed outside of the strand. As there are multiple threads running the server, the program fails to ensure that all operations are performed within the same strand.

void connection::async_write(...)
{
  ...
  // `strand_` protects `message_queue_` and guarantees that writes to
  // `socket_` are only occurring within the strand.
  strand_.post([...]()
    {
      auto empty = message_queue_.empty();
      message_queue_.push([=]()
        {
          ...
          // Intermediate and final handlers are performed within `strand_`.
          boost::asio::async_write(socket_, ... strand_.wrap([...](...)
            {
              ...
              message_queue_.front()()
            }));
        });
      if (empty)
        message_queue_.front()();
    });
}

void connection::async_read(...)
{
  // Not performed within `strand_`, violating thread safety.
  boost::asio::async_read(socket_, ...,
    // Neither intermediate, final handler, nor subsequent read
    // operations are performed within `strand_`.
    [](...)
    {
      boost::asio::async_read(socket_, ...);
    }
  });
}

To resolve this, explicitly perform the async_read operations from within the connection::strand_ strand. Furthermore, the read handlers for the operations should be wrapped within the connection::strand_ as well:

void connection::async_read(...)
{
  strand_.post([...](...)
    {
      // async_read is now performed with the strand.
      boost::asio::async_read(socket_, ...,
        // Intermediate and final handlers are performed within the strand.
        strand_.wrap([](...)
          {
            boost::asio::async_read(socket_, ...);
          });
    });
}

For more details on how to guarantee that all asynchronous operations and handlers performed within a strand, see this answer.

Community
  • 1
  • 1
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • thank you! i just assumed i had to assure that there are not 2 concurrent reads/writes and not that read and write are not allowed to be concurrent either – ACB Dec 22 '16 at 19:45