Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it correct to call semget() followed by semop() without calling semctl()?

Tags:

c

semaphore

ipc

I was going through the example program for semaphores in Beej's Guide to Unix IPC.

The example program contains the following semaphore initialization code. I am only posting a snippet from it that is relevant to the question. To see the complete code, visit the link I have provided above.

/*
** initsem() -- more-than-inspired by W. Richard Stevens' UNIX Network
** Programming 2nd edition, volume 2, lockvsem.c, page 295.
*/
int initsem(key_t key, int nsems)  /* key from ftok() */
{
    int i;
    union semun arg;
    struct semid_ds buf;
    struct sembuf sb;
    int semid;

    semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666);

    if (semid >= 0) { /* we got it first */
        sb.sem_op = 1; sb.sem_flg = 0;
        arg.val = 1;

        printf("press return\n"); getchar();

        for(sb.sem_num = 0; sb.sem_num < nsems; sb.sem_num++) { 
            /* do a semop() to "free" the semaphores. */
            /* this sets the sem_otime field, as needed below. */
            if (semop(semid, &sb, 1) == -1) {
                int e = errno;
                semctl(semid, 0, IPC_RMID); /* clean up */
                errno = e;
                return -1; /* error, check errno */
            }
        }

Here is what I am unable to understand. Once semget() creates semaphores and returns successfully with a valid semaphore ID, the semaphores themselves are uninitialized and in an indeterminate state. This is confirmed by the man page of semget.

Semaphore initialization

The values of the semaphores in a newly created set are indeterminate. (POSIX.1-2001 and POSIX.1-2008 are explicit on this point, although POSIX.1-2008 notes that a future version of the standard may require an implementation to initialize the semaphores to 0.) Although Linux, like many other implementations, initializes the semaphore values to 0, a portable application cannot rely on this: it should explicitly initialize the semaphores to the desired values.

Initialization can be done using semctl(2) SETVAL or SETALL operation. Where multiple peers do not know who will be the first to initialize the set, checking for a nonzero sem_otime in the associated data structure retrieved by a semctl(2) IPC_STAT operation can be used to avoid races.

But in the above code semctl() was not called to initialize the semaphores. Instead, semop() was called with sem_op = 1, which according to the man page of semop does the following.

If sem_op is a positive integer, the operation adds this value to the semaphore value (semval). Furthermore, if SEM_UNDO is specified for this operation, the system subtracts the value sem_op from the semaphore adjustment (semadj) value for this semaphore. This operation can always proceed—it never forces a thread to wait. The calling process must have alter permission on the semaphore set.

But without initialization, semval was indeterminate. So adding 1 to it still leaves it indeterminate.

Is this code incorrect or is my understanding incorrect? If my understanding is incorrect, could you please explain me why this code is correct?

like image 386
Lone Learner Avatar asked Feb 05 '23 16:02

Lone Learner


1 Answers

Your understanding is correct, the code is not portable. The POSIX.1-2008 specification of semget does not require the semaphore to be initialized:

Upon creation, the semid_ds data structure associated with the new semaphore identifier is initialized as follows:

  • In the operation permissions structure sem_perm.cuid, sem_perm.uid, sem_perm.cgid, and sem_perm.gid shall be set to the effective user ID and effective group ID, respectively, of the calling process.

  • The low-order 9 bits of sem_perm.mode shall be set to the low-order 9 bits of semflg.

  • The variable sem_nsems shall be set to the value of nsems.

  • The variable sem_otime shall be set to 0 and sem_ctime shall be set to the current time, as described in IPC General Description.

  • The data structure associated with each semaphore in the set need not be initialized. The semctl() function with the command SETVAL or SETALL can be used to initialize each semaphore.

like image 156
Tim Avatar answered Apr 08 '23 07:04

Tim