Search code examples
csynchronizationqueuefork

A problem with synchronization of the TAILQ queues in the parent and child processes in C


There are 5 children and 1 parent process (to simplify, we take only 1 child). Some calculations take place in the child processes, after which they have to pass the information to the parent. I use a TAILQ queue for messaging. The problem is that running the program below leads to a completely empty output. Despite the fact that the queue is initialized before forking, we get as if two instances of the same queue that are not in contact with each other. And if you call TAILQ_FIRST from a child process, everything works fine. How do I synchronize queues?

#define _SVID_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/queue.h>

struct message
{
    int num;
    char status;
    TAILQ_ENTRY(message) queue_entry;
};

struct message_queue
{
    TAILQ_HEAD(head_t, message) head;
} queue;


int main()
{
    TAILQ_INIT(&queue.head);

    pid_t pid;
    pid = fork();

    if (!pid) // child process
    {
        while(1)
        {
            struct message* p = malloc(sizeof(struct message));
            p->num = 1;
            p->status = 's';
            TAILQ_INSERT_TAIL(&queue.head, p, queue_entry);
        }
    }
    else // parent process
    {
        while (1)
        {
            struct message* p = TAILQ_FIRST(&queue.head);

            if (p)
            {
                TAILQ_REMOVE(&queue.head, p, queue_entry);
            }

            if (p)
            {
                printf("Num %d and status %c", p->num, p->status);
                free(p);
            }
        }

    }

    return 0;
}

Solution

  • Expanding on my comment; the TAILQ mechanism only works within a single process, not across parent/child processes.

    we get as if two instances of the same queue that are not in contact with each other.

    Yes, you understand this correctly.

    When you call fork(), it actually creates a second process identical to the first, with a copy of all program and memory areas, and the only difference is the return value of the pid variable: it's zero in the child and nonzero in the parent (well, <0 is an error as well).

    This means that upon the fork, there are now two private copies of the queue, and each process is operating "correctly" on it, but there is no cooperating process that manipulates the other end.

    I remember very well trying to get my head around the concept of a single function behaving this way :-)

    In your example, the child process is able to add messages over and over, but there's no consumer. The parent is waiting for something to happen, but it never will.

    If your task is limited to parent and child processes talking to each other, a pipe is a good mechanism for this, but if you need more general interprocess communications, then a POSIX message queue is a fair choice, but there are likely others.