clinuxmultithreadingsemaphore

finding out how many processes are waiting on named semaphore


I found a toy program here (http://stackoverflow.com/a/16400833) to check out named semaphores. I modified it somewhat, opening the named semaphore in each child thread instead of in the parent program. After the sem_wait() I did a sem_getvalue(), then a sleep(1), then sem_post(), and another sem_getvalue().

After the wait, the value retrieved is 0. After the post, the value retrieved is 1.

I initialized the semaphore value at 1, so as to allow only one child thread to run at a time, so this makes sense. Wait decrements it to 0, post increments it again (and lets the next thread proceed).

However, I also did a hexdump of the semaphore file in /dev/shm/sem.whatever, using "watch", and as the child threads posted to release the semaphore, there was a count decrementing in that semaphore file.

hexdump of semaphore file

I think that value there circled in red was being incremented by the waits, so basically counting the number of threads waiting on the semaphore. A post would then decrement it. You could watch it decrease as the test program ran. This is not the "value" being retrieved by sem_getvalue().

Anyway, it seems like the count of waiting threads is available in the semaphore file itself, but I don't see anything in the sem_xxx() api to retrieve it. As I said, sem_value() seems to get me only either 0 or 1.

Am I missing something obvious?

Ok, I was told to edit the question to include code.

First, here's the command I used in a second window to monitor the internals of the semaphore file. (the PNG image is of this):

watch -d -n 0.1 'ls -tlr /dev/shm ; hexdump /dev/shm/sem.*'

I run this while the semaphore test program is running in another window. Since the child threads do a sleep after the wait, this slows the processing down enough to watch the mystery number (see image above) decrement as each new thread wakes up to do it's work.

Here is the altered semaphore test program. The primary change is that the child threads do their own opening of the semaphore, rather than the parent opening it (and immediately unlinking it). This toy program can easily tell when all threads are done and can remove the semaphore ... but the situation in the production environment are not so clean. Hence my wishing to be able to detect when every process is done with the semaphore.


#include <stdio.h>          /* printf()                 */
#include <stdlib.h>         /* exit(), malloc(), free() */
#include <unistd.h>
#include <sys/types.h>      /* key_t, sem_t, pid_t      */
#include <sys/wait.h>
#include <sys/shm.h>        /* shmat(), IPC_RMID        */
#include <errno.h>          /* errno, ECHILD            */
#include <semaphore.h>      /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h>          /* O_CREAT, O_EXEC          */

#define SEM    "pSem"

/*
https://gist.github.com/junfenglx/7412986
*/

int main (int argc, char **argv){
    int i;                        /*      loop variables          */
    key_t shmkey;                 /*      shared memory key       */
    int shmid;                    /*      shared memory id        */
    sem_t *sem;                   /*      synch semaphore         *//*shared */
    pid_t pid;                    /*      fork pid                */
    int *p;                       /*      shared variable         *//*shared */
    unsigned int n;               /*      fork count              */
    unsigned int value;           /*      semaphore value         */

    /* initialize a shared variable in shared memory */
    shmkey = ftok ("/dev/null", 5);       /* valid directory name and a number */
    printf ("shmkey for p = %d\n", shmkey);
    shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
    if (shmid < 0){                           /* shared memory error check */
        perror ("shmget\n");
        exit (1);
    }

    p = (int *) shmat (shmid, NULL, 0);   /* attach p to shared memory */
    *p = 0;
    printf ("p=%d is allocated in shared memory.\n\n", *p);

    /********************************************************/

    printf ("How many children do you want to fork?\n");
    printf ("Fork count: ");
    scanf ("%u", &n);

    printf ("What do you want the semaphore value to be?\n");
    printf ("Semaphore value: ");
    scanf ("%u", &value);

    /* initialize semaphores for shared processes */


    /* fork child processes */
    for (i = 0; i < n; i++){
        pid = fork ();
        if (pid < 0)              /* check for error      */
            printf ("Fork error.\n");
        else if (pid == 0)
            break;                  /* child processes */
        else {
            printf("thread #%d: pid=%d\n", i, pid);
        }
    }


    /******************************************************/
    /******************   PARENT PROCESS   ****************/
    /******************************************************/
    if (pid != 0){
         int ret = 0;

        /* wait for all children to exit */

        while (pid = waitpid (-1, NULL, 0)){
            if (errno == ECHILD)
                break;
        }

        printf ("\nParent: All children have exited.\n");

         ret = sem_unlink(SEM);
         if (ret != 0) {
            printf("\nParent: unlink returned %d\n", ret, errno);
         }


        /* shared memory detach */
        shmdt (p);
        shmctl (shmid, IPC_RMID, 0);

        /* cleanup semaphores */
        printf("sem_destroy return value:%d\n",sem_destroy (sem));
        exit (0);
    }

    /******************************************************/
    /******************   CHILD PROCESS   *****************/
    /******************************************************/
    else{
      int tid = pid; // gettid();

      // each thread opens the semaphore itself
       sem = sem_open (SEM, O_CREAT, 0644, value);

      if (sem == SEM_FAILED) {
         printf("child #%d, %d: sem_open failed, errno=%d\n", i, tid, errno);
         exit;
      }

       /* name of semaphore is "pSem", semaphore is reached using this name */
       /* if a crash occurs during the execution         */
       printf ("#%d: semaphore initialized.\n\n", i);


         int value=0;
         int ret = 0;


        sem_wait (sem);           /* P operation */
        printf ("  Child(%d) is in critical section.\n", i);
         ret = sem_getvalue(sem, &value);
         printf("  #%d: ret=%d, value=%d\n", i, ret, value);
        sleep (1);
        *p += i % 3;              /* increment *p by 0, 1 or 2 based on i */
        printf ("  Child(%d) new value of *p=%d.\n", i, *p);
        sem_post (sem);           /* V operation */
         ret = sem_getvalue(sem, &value);
         printf("  #%d: ret=%d, value=%d\n", i, ret, value);
        exit (0);
    }
}
/*src:http://stackoverflow.com/a/16400833*/


Solution

  • POSIX says of sem_getvalue():

    If sem is locked, then the object to which sval points shall either be set to zero or to a negative number whose absolute value represents the number of processes waiting for the semaphore at some unspecified time during the call.

    However, the Linux sem_getvalue(2) manpage says that its implementation uses the first of those behaviors, always setting the returned count to 0. So even if it's tracked internally (Which is an implementation detail you shouldn't rely on), it's not information that's made publicly available on Linux systems. You're not missing something; it's not there to find.