I have a c++17 project using the non-boost version of ASIO because i need to connect, read and write to a TCP socket. The application has a read and write thread that run periodically and share a mutex therefore my reading thread has a time slot of 20 milliseconds in which it needs to read as much as it can and exit.
My problem is that i cant figure out how to get ASIO to read and then stop reading gracefully until another read is requested. There is no read with timeout functions and neither could i find any examples of such behaviour.
The closest thing ive found seems to kinda work but not exactly and i have no idea why. My current code is something like this:
ErrorCode Read(uint8_t* buf, unsigned int maxAmountOfBytesToRead, unsigned int& nRead)
{
std::lock_guard<std::mutex> tcpSocketLock(m_TCPSocketMutex);
asio::error_code asioError;
unsigned int amountOfBytesInBuffer = 0;
m_TCPConnectionSocket.async_read_some(asio::buffer(buf, maxAmountOfBytesToRead),
[&](const asio::error_code& errorCode, unsigned int result_n)
{
asioError = errorCode;
amountOfBytesInBuffer = result_n;
});
RunIOContextWithTimeOut(std::chrono::milliseconds(20));
nRead = amountOfBytesInBuffer;
// finish up and exit.
}
void RunIOContextWithTimeOut(std::chrono::steady_clock::duration timeout)
{
// Restart the io_context, as it may have been left in the "stopped" state
// by a previous operation.
m_TCPioContext.restart();
// Block until the asynchronous operation has completed, or timed out. If
// the pending asynchronous operation is a composed operation, the deadline
// applies to the entire operation, rather than individual operations on
// the socket.
m_TCPioContext.run_for(timeout);
// If the asynchronous operation completed successfully then the io_context
// would have been stopped due to running out of work. If it was not
// stopped, then the io_context::run_for call must have timed out.
if (!m_TCPioContext.stopped())
{
m_TCPioContext.stop();
// Run the io_context again until the operation completes.
m_TCPioContext.run();
}
}
But when running this code, i do notice that the data coming in is not exactly correct and that there are chunks of it missing. Adding logs and debugging i see that when the run_for pops out because of a time out, it never finishes the async read callback handler which makes me suspect that when the run_for doesnt finish on its own and is asked to stop, it abandons what ever data is has read and exits.
But i thought that was what the subsequent run() function was used for, to make the thread go back in and finish running the read before exiting. But apparently not? I dont understand how to make it just read and when its time to stop, copy over all that has read and stop gracefully. All other examples have you closing sockets and cancelling everything but i want to keep the socket open, the connection established, just to stop reading.
I cant let it read for as long as it wants because there is a write thread waiting for the read to finish so that it can be executed. I also would prefer not to make a solution that uses an additional thread of continues reading because this solution will be scaled up which will cause the usage of an additional 40 threads on a system with limited resources, we want to be as efficient as possible with our CPU resources.