sowdust
sowdust

Reputation: 87

Synchronized system through unix ME and counter semaphores

I have a very hard time working with synchronization. My current architecture needs to be like this:

N working processes work in mutual exclusion over a shared segment of memory. When all of them have done their job, 1 "screen" process shows the shared memory status

It doesn't matter in which order the N processes work, they are all equal, but they all need to work ONCE on the shared memory BEFORE (and AFTER) the "screen" process has done his job (hence in mutual exclusion as well).

I thought of organizing the processes like this: I create two semaphores, one binary for the mutual exclusion (either 1 or 0) and one that works as a counter (its value can go from 0 to the # of processes).

I will use the standard letters to mean standard operations on semaphores:

P & V are the traditional wait and signal

Z(sem) = wait for zero on sem (used only on the sem_counter)

R(sem) = reset sem (used only for the sem_counter)

This is the code of the common function operating on semaphores:

int P(int semid, int semnum)    // signal
{
        struct sembuf cmd;
        cmd.sem_num = semnum;
        cmd.sem_op = -1;
        cmd.sem_flg = 0;
        semop(semid, &cmd, 1);
        return 1;

}


int V(int semid, int semnum)    // wait
{
        struct sembuf cmd;
        cmd.sem_num = semnum;
        cmd.sem_op = 1;
        cmd.sem_flg = 0;
        semop(semid, &cmd, 1);
        return 0;
}

int Z(int semid, int semnum)    // wait for zero
{
        struct sembuf cmd;
        cmd.sem_num = semnum;
        cmd.sem_op = 0;
        cmd.sem_flg = 0;
        if(semop(semid, &cmd, 1) < 0)
        {
            fprintf (stderr, "Errore inizializzazione del semaforo\n%s\n",
                strerror(errno) );

        }
        return 0;
}

int R(int semid, int semnum, int val)   // reset
{

    union semun arg;
    arg.val = val;
    if( semctl(semid,semnum,SETVAL,arg) == -1 )  
    {
        fprintf (stderr, "Errore inizializzazione del semaforo\n%s\n",
                strerror(errno) );
    }
}

This is the code of the "showing" process that resets the counter semaphore:

while( 1 )
{

    printf("Initialization \n");

    // signal ME semaphore
    inuso = P(sem_id,0);

    printf("Doing work on the shared memory \n");

    //  reset counter semaphore
    R(sem_id_counter,0,N_PROC_DEFAULT);

    printf("Again doing work on the shared memory \n");

    // release ME semaphore
    inuso = V(sem_id,0);

    // wait for sem counter to become 0
    Z(sem_id_counter,0); 

}

This is the code of the working processes that diminish the counter semaphore:

while(1)
{

// try to diminish counter semaphore
P(sem_id_counter,0);

// asks for ME semaphore
inuso=P(sem_id,0);

printf("Doing stuff on the shared memory");

// releases ME semaphore
inuso=V(sem_id,0);

// waits for other processes to finish (4 sem_counter 2 become 0)
Z(sem_id_counter,0);

}

The problem is that they eventually get stuck; this doesn't happen if I remove the Z from the "working process" (ie. I don't make them wait for the others before trying again).

Upvotes: 0

Views: 307

Answers (1)

Armali
Armali

Reputation: 19375

The problem is that they eventually get stuck

Consider this course of events:

  • The last of the working processes has just returned from P(sem_id_counter,0); that leaves the counter semaphore at value zero.
  • Now the "showing" process is ready to run and immediately executes the return from

    Z(sem_id_counter,0);
    

    as well as

    printf("Initialization \n");
    inuso = P(sem_id,0);
    

    before the working process gets to execute P(sem_id,0); thus blocking the working process until after the counter semaphore is reset to N_PROC_DEFAULT, say 5.

  • After that, the counter semaphore is decremented to not less than 1 by the other working processes that were already waiting in Z(), since the above working process has already passed over its P(sem_id_counter,0);
  • at the end, all processes wait for the counter semaphore to reach zero, which never happens.

In fact, similar deadlocks can occur whenever the counter semaphore may be reset to N_PROC_DEFAULT without each working process waiting or already having waited for the zero count. One way to ensure that this doesn't happen is to test (at the start of the "showing" process loop) the number of processes waiting for an increase of semval in P(sem_id_counter,0), and proceed only after that number is N_PROC_DEFAULT:

    // wait until all workers are waiting in P(sem_id_counter,0)
    struct timespec req = { 0, 100000000 }; // 0.1 s
    while (semctl(sem_id_counter, 0, GETNCNT) < N_PROC_DEFAULT) 
        nanosleep(&req, NULL);

    printf("Initialization \n");
…

Alternatively, if you don't want to poll for the workers, you could make sem_id_counter an array of 2 semaphores and use the additional counting semaphore to signal that the workers have waited for zero on the primary counter, i. e.

  • in R() set both counters to N_PROC_DEFAULT (with cmd SETALL)

  • at the end of the "showing" process loop do Z(sem_id_counter, 1) instead of Z(…, 0)

  • at the end of the working process loop do P(sem_id_counter, 1).

Upvotes: 2

Related Questions