I'm trying to understand a bit better how async asio works.
I have the following code, where I'm calling async_read on a socket to read the next 10 bytes of data.
struct SocketReader {
void do_read_body()
{
asio::async_read(socket_,
asio::buffer(msg_, 10),
[this](asio::error_code ec, std::size_t length)
{
if (!ec)
{
//messages_to_work_on.emplace_back(msg_); // <-- I'm trying to send this msg_ instance to another io_context
do_read_body(); // call again
}
else
{
socket_.close();
}
});
}
std::vector<uint8_t> msg_;
asio::tcp::socket _socket;
}
These reads are done inside an io_context running in his own std::thread, where I'm collecting in a queue all messages read from the socket. So far so good.
I have also another "worker" class that just executes some work based on what is available in his queue:
struct Worker
{
asio::io_context& io_context_;
std::deque< std::vector<uint8_t> > queue;
Worker(asio::io_context& io_context)
: io_context_(io_context) {
asio::post(io_context_, [this]() {doWork();});
}
void doWork() {
if (!queue.empty())
{
// do some work with front()
queue.pop_front();
}
asio::post(io_context_, [this]() {doWork();});
}
};
That one is also executing in his own io_context, running in his own thread. So there is concurrency between the socket thread and the worker thread.
What is the correct way to post the data received from the socket, to the worker class ? I'm thinking I should be able to call from the socket completion handler, something like:
asio::post(worker_io_context, [this]() {worker.queue.push_back(msg_)});
That way, I'm at least sure that the worker queue is not used concurently. But I'm not sure if I'm allowed to post from one io_context to the other, and also if I won't create another race condition this way. I also don't really understand where the memory for my message should be located, especially "in between" the transfer from one io_context to the other. Is it required I pass the message by value (since this.msg_ can be modified before the post handler is executed) ?
Thanks!