Search code examples
clinuxforkipcmessage-queue

Issue with fork() and message queue communication in C program


In my program, I'm attempting to fork a process when it receives the message "ON" from a message queue. Before introducing the fork, the messages work correctly:

    while (1)
    {
        receive_message(msg_queue_id, data, sizeof(data), getpid());
        if (strcmp(data, "ON") == 0)
        {
            puts("Received");
        }
    }

However, upon adding the fork, the program exhibits inconsistent behavior. The initial 10 processes seem to be affected, and it's worth noting that the fork doesn't always fail; there are instances where it succeeds.

    while (1)
    {
        receive_message(msg_queue_id, data, sizeof(data), getpid());
        if (strcmp(data, "ON") == 0)
        {
            pid = fork();
            CATCH_ERROR;
            if (pid == 0)
            {
                puts("Received");
            }
        }
    }

The error message encountered when the fork fails is as follows:

[ERROR] src/proc.c Line: 32 PID = 22755 Error 42 (No message of desired type)

I'm struggling to understand why forking the process intermittently causes issues with the message queue. Any insights or suggestions on potential causes and how to address them would be greatly appreciated.

My expectation was that forking the process would not interfere with the message queue operations, but the intermittent "No message of desired type" error suggests otherwise. I'm looking for insights into potential causes or alternative approaches to resolve this issue.

    void receive_message(int msg_id, void *msg_ptr, size_t msg_size, int msg_type)
{
    struct msqid_ds buf;
    struct msgbuf message;
    message.mtype = msg_type;

    if (msgctl(msg_id, IPC_STAT, &buf) == -1)
    {
        if (errno != ENOMSG)
            CATCH_ERROR;

        return;
    }

    if (buf.msg_qnum == 0)
        return;

    if (msgrcv(msg_id, &message, msg_size, msg_type, IPC_NOWAIT) == -1)
    {
        if (errno != ENOMSG)
            CATCH_ERROR;

        return;
    }

    memcpy(msg_ptr, message.mtext, msg_size);
}

Solution

  • Suppose a process reads "ON" from the message queue. It writes "Received" and then attempts to read another message. If msgrcv returns -1 and sets errno to ENOMSG, receive_message returns without writing any data to msg_ptr and data still contains "ON" from the previous read. strcmp again returns 0, fork is called and both the parent and the child immediately invoke the CATCH_ERROR macro which (presumably) writes an error message based on what is currently in errno. If neither strcmp nor fork have modified errno, then that value is still ENOMSG, and you see the offending error message.