cmultithreadingsemaphoresysv-ipc

Counting semaphore using SysV


I am trying to understand the concept of counting semaphore through an example. But I want to implement this using SysV in Linux.
I am familiar with theoretical part of binary semaphore and counting semaphore.
I have referred this link.

Conceptually, semaphores are used as a signaling mechanism from one process to another, so I was trying to write a simple program.

In the below program, I want thread_1 to wait till it doesn't get a signal from thread_2 and similarly thread_2 should wait till it doesn't get a signal from thread_3.

So that the output should be something like this: Hello From thread 3 Hello from thread 2 Hello from thread 1

I know it can be achieved using pthread_join() properly but I want to achieve it using semaphores.

Code:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>

int sem_id;
struct sembuf sops[3];
void thread_1(void)
{
    sops[0].sem_num = 0;
    sops[0].sem_op = 0;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 3");
    else
        printf("Hello From thread 1\n");
}

void thread_2(void)
{
    sops[0].sem_num = 0;
    sops[0].sem_op = -1;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 2");
    else
        printf("Hello from thread 2\n");
}

void thread_3(void)
{

    sops[0].sem_num = 0;
    sops[0].sem_op = -1;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 3");
    else
        printf("Hello from thread 3\n");
}

int main(void)
{
    void (*funct[]) = {thread_1, thread_2, thread_3};

    key_t semkey;
    char i;
    union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           }arg;
    pthread_t thread_id[3];

    semkey = ftok("/tmp", 'a');

    if(semkey < 0)
        perror("Cannot Create Semaphore Key");
    else
    {
        sem_id = semget(semkey, 1, (IPC_CREAT|IPC_EXCL|0666));
        if(sem_id < 0)
            perror("Cannot create semaphore\n");
        else
        {
            arg.val = 3;
            if (semctl(sem_id, 0, SETVAL, arg) == -1) {
                perror("semctl");
                exit(1);
             }   
        }
    }

    for(i = 0; i < 3; i++)
    {
        if(pthread_create(&thread_id[i], NULL, funct[i], NULL) < 0)
            perror("Cannot Create thread\n");
    }

   for(i = 0; i < 3; i++)
        pthread_join(thread_id[i], NULL);

    if(semctl(sem_id, 0, IPC_RMID, NULL) == -1)
        perror("semctl");

    return 0;
}

Do I have to use more than one semaphore set to achieve what I am trying to do?


Solution

  • You need two mutexes / semaphores with count 1. Assuming your threads are called t0,t1,t2 and your semaphores sem0 and sem1, then t0 runs freely and increments sem0, t1 waits on sem0 and increments sem1, and t2 waits on sem1.

    Here's a complete draft without error checking:

    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <errno.h>
    
    static int sem0, sem1;
    #define POST1(Sem) semop(Sem, &(struct sembuf){0,1,0}, 1)
    #define WAIT1(Sem) semop(Sem, &(struct sembuf){0,-1,0}, 1)
    
    static void* t0(void *unused) { puts("hello from t0"); POST1(sem0); return 0; }
    static void* t1(void *unused) { WAIT1(sem0); puts("hello from t1"); POST1(sem1); return 0; }
    static void* t2(void *unused) { WAIT1(sem1); puts("hello from t2"); return 0; }
    
    int main(void)
    {
        key_t sem0_k, sem1_k;
        sem0_k = ftok("/tmp", '0');
        sem1_k = ftok("/tmp", '1');
    
        sem0 = semget(sem0_k, 1, (IPC_CREAT|IPC_EXCL|0666));
        sem1 = semget(sem1_k, 1, (IPC_CREAT|IPC_EXCL|0666));
    
        pthread_t tids[3];
        pthread_create(tids+2, NULL, t2, NULL);
        sleep(1);
        pthread_create(tids+1, NULL, t1, NULL);
        sleep(1);
        pthread_create(tids+0, NULL, t0, NULL);
    
        for(int i = 0; i < 3; i++)
            pthread_join(tids[i], NULL);
    
        semctl(sem0, 0, IPC_RMID, NULL);
        semctl(sem1, 0, IPC_RMID, NULL);
    
        return 0;
    }
    

    I'm running the threads in reverse order and with 1 second waits in between t0 and t1, and t1 and t2 to show the semaphores do the job of ordering the threads from t0 to t2.