1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Use async_write to prevent locks if receiver is too slow

This commit is contained in:
Ivan Savenko 2024-05-11 08:46:00 +00:00
parent fc4196e71c
commit 4c3aac8f19
2 changed files with 39 additions and 8 deletions

View File

@ -105,17 +105,44 @@ void NetworkConnection::onPacketReceived(const boost::system::error_code & ec, u
void NetworkConnection::sendPacket(const std::vector<std::byte> & message) void NetworkConnection::sendPacket(const std::vector<std::byte> & message)
{ {
std::lock_guard<std::mutex> lock(writeMutex); std::lock_guard<std::mutex> lock(writeMutex);
std::vector<std::byte> headerVector(sizeof(uint32_t));
uint32_t messageSize = message.size();
std::memcpy(headerVector.data(), &messageSize, sizeof(uint32_t));
boost::system::error_code ec; bool messageQueueEmpty = dataToSend.empty();
dataToSend.push_back(headerVector);
// create array with single element - boost::asio::buffer can be constructed from containers, but not from plain integer
std::array<uint32_t, 1> messageSize{static_cast<uint32_t>(message.size())};
boost::asio::write(*socket, boost::asio::buffer(messageSize), ec );
if (message.size() > 0) if (message.size() > 0)
boost::asio::write(*socket, boost::asio::buffer(message), ec ); dataToSend.push_back(message);
//Note: ignoring error code, intended if (messageQueueEmpty)
doSendData();
//else - data sending loop is still active and still sending previous messages
}
void NetworkConnection::doSendData()
{
if (dataToSend.empty())
throw std::runtime_error("Attempting to sent data but there is no data to send!");
boost::asio::async_write(*socket, boost::asio::buffer(dataToSend.front()), [self = shared_from_this()](const auto & error, const auto & )
{
self->onDataSent(error);
});
}
void NetworkConnection::onDataSent(const boost::system::error_code & ec)
{
std::lock_guard<std::mutex> lock(writeMutex);
dataToSend.pop_front();
if (ec)
{
logNetwork->error("Failed to send package: %s", ec.message());
// TODO: Throw?
return;
}
if (!dataToSend.empty())
doSendData();
} }
void NetworkConnection::close() void NetworkConnection::close()

View File

@ -18,6 +18,7 @@ class NetworkConnection : public INetworkConnection, public std::enable_shared_f
static const int messageHeaderSize = sizeof(uint32_t); static const int messageHeaderSize = sizeof(uint32_t);
static const int messageMaxSize = 64 * 1024 * 1024; // arbitrary size to prevent potential massive allocation if we receive garbage input static const int messageMaxSize = 64 * 1024 * 1024; // arbitrary size to prevent potential massive allocation if we receive garbage input
std::list<std::vector<std::byte>> dataToSend;
std::shared_ptr<NetworkSocket> socket; std::shared_ptr<NetworkSocket> socket;
std::mutex writeMutex; std::mutex writeMutex;
@ -27,6 +28,9 @@ class NetworkConnection : public INetworkConnection, public std::enable_shared_f
void onHeaderReceived(const boost::system::error_code & ec); void onHeaderReceived(const boost::system::error_code & ec);
void onPacketReceived(const boost::system::error_code & ec, uint32_t expectedPacketSize); void onPacketReceived(const boost::system::error_code & ec, uint32_t expectedPacketSize);
void doSendData();
void onDataSent(const boost::system::error_code & ec);
public: public:
NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr<NetworkSocket> & socket); NetworkConnection(INetworkConnectionListener & listener, const std::shared_ptr<NetworkSocket> & socket);