I am trying to figure out a good naming scheme for Boost Interprocess message_queue
's. Suppose there is a group of processes all using the same queue. There may be multiple simultaneous 'runs' of this group of processes. I assume it is required for each run to use a unique name per queue that is used amongst the group of processes. The following prototype code sends a bunch of messages to a server process that reads the messages from a queue and then prints them:
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/serialization/string.hpp>
#include <iostream>
#include <vector>
#include <unistd.h>
namespace ipc = boost::interprocess;
constexpr unsigned messages{1000000};
constexpr unsigned maxMessageSize{8192};
struct Message
{
std::string prefix;
template<typename Archive>
void serialize(Archive& ar, const unsigned int)
{
// clang-format off
ar & prefix;
// clang-format on
}
};
int loggerServerMain()
{
try
{
// Open a message queue.
ipc::message_queue mq(ipc::open_only, "message_queue");
unsigned int priority;
ipc::message_queue::size_type recvd_size;
for (int i = 0; i < messages; ++i)
{
std::string data;
data.resize(maxMessageSize);
mq.receive(&data[0], maxMessageSize, recvd_size, priority);
if (recvd_size > maxMessageSize)
return 1;
std::istringstream iss{data};
boost::archive::binary_iarchive ia{iss};
Message message;
ia >> message;
std::cout << message.prefix << '\n';
}
}
catch (const ipc::interprocess_exception& ex)
{
std::cout << ex.what() << std::endl;
return 1;
}
ipc::message_queue::remove("message_queue");
return 0;
}
int main()
{
try
{
ipc::message_queue::remove("message_queue");
ipc::message_queue mq(ipc::create_only, "message_queue", 100, maxMessageSize);
if (fork() == 0)
{
exit(loggerServerMain());
}
for (int i = 0; i < messages; ++i)
{
Message message{"lib" + std::to_string(i)};
std::ostringstream oss;
boost::archive::binary_oarchive oa{oss};
oa << message;
std::string data{oss.str()};
mq.send(data.data(), data.size(), 0);
}
}
catch (const ipc::interprocess_exception& ex)
{
std::cout << ex.what() << std::endl;
return 1;
}
return 0;
}
So we have a process that forks a server, then proceeds to send a whole bunch of messages to the server which in turn will print the messages. We use a hardcoded name 'message_queue' for the name of the underlying message queue.
I was surprised to find out that this example still works when multiple instances of this process are started simultaneously, for example as follows:
./message_queue > 1.log & ; sleep 1 ; ./message_queue > 2.log
Both logs contain all messages, in the right order. The first instance of message_queue
still runs when the second one is started. Both logs are created and written to simultaneously.
How can it be that there is no contention over the message queue when used from more process 'groups' simultaneously? I assumed the name of the message queue is some global defined at the system level, but from my example it seems this is defined per process tree somehow? I digged through the documentation of Boost Interprocess but could not find any mention of this.
I am using Linux 4.20, GCC 8.1.0. The example code is compiled as follows:
g++ -std=c++17 -O3 -o message_queue message_queue.cpp -lpthread -lboost_serialization -lrt