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.