14.4 POSIX:SEM Semaphore Operations

Team-FLY

The semaphore operations described in this section apply both to POSIX:SEM unnamed semaphores and to POSIX:SEM named semaphores described in Section 14.5.

The sem_post function implements classic semaphore signaling. If no threads are blocked on sem , then sem_post increments the semaphore value. If at least one thread is blocked on sem , then the semaphore value is zero. In this case, sem_post causes one of the threads blocked on sem to return from its sem_wait function, and the semaphore value remains at zero. The sem_post function is signal-safe and can be called from a signal handler.

  SYNOPSIS  #include <semaphore.h>     int sem_post(sem_t *sem);  POSIX:SEM  

If successful, sem_post returns 0. If unsuccessful , sem_post returns “1 and sets errno . The sem_post operation sets errno to EINVAL if *sem does not correspond to a valid semaphore.

The sem_wait function implements the classic semaphore wait operation. If the semaphore value is 0, the calling thread blocks until it is unblocked by a corresponding call to sem_post or until it is interrupted by a signal. The sem_trywait function is similar to sem_wait except that instead of blocking when attempting to decrement a zero-valued semaphore, it returns “1 and sets errno to EAGAIN .

  SYNOPSIS  #include <semaphore.h>     int sem_trywait(sem_t *sem);     int sem_wait(sem_t *sem);  POSIX:SEM  

If successful, these functions return 0. If unsuccessful, these functions return “1 and set errno . These functions set errno to EINVAL if *sem does not correspond to a valid semaphore. The sem_trywait sets errno to EAGAIN if it would block on an ordinary sem_wait .

The sem_wait and sem_trywait functions may set errno to EINTR if they are interrupted by a signal. Any program that catches signals must take care when using semaphore operations, since the standard allows sem_wait and sem_trywait to return when a signal is caught and the signal handler returns. Program 14.2 restarts the sem_wait if it is interrupted by a signal.

Program 14.2 shows how to implement a shared variable that is protected by semaphores. The initshared function initializes the value of the shared variable. It would normally be called only once. The getshared function returns the current value of the variable, and the incshared function atomically increments the variable. If successful, these functions return 0. If unsuccessful, these functions return “1 and set errno . The shared variable ( shared ) is static, so it can be accessed only through the functions of semshared.c . Although shared is a simple integer in Program 14.2, functions of the same form can be used to implement any type of shared variable or structure.

Program 14.2 semshared.c

A shared variable protected by semaphores .

 #include <errno.h> #include <semaphore.h> static int shared = 0; static sem_t sharedsem; int initshared(int val) {     if (sem_init(&sharedsem, 0, 1) == -1)         return -1;     shared = val;     return 0; } int getshared(int *sval) {     while (sem_wait(&sharedsem) == -1)         if (errno != EINTR)             return -1;     *sval = shared;     return sem_post(&sharedsem); } int incshared() {     while (sem_wait(&sharedsem) == -1)         if (errno != EINTR)             return -1;     shared++;     return sem_post(&sharedsem); } 
Exercise 14.15

Suppose a variable were to be incremented in the main program and also in a signal handler. Explain how Program 14.2 could be used to protect this variable.

Answer:

It could not be used without some additional work. If the signal were caught while a call to one of the functions in Program 14.2 had the semaphore locked, a call to one of these in the signal handler would cause a deadlock. The application should block the signals in the main program before calling getshared and incshared .

Programs 14.3 and 14.4 return to the original critical section problem of Program 14.1. The new version uses threads to illustrate the need to protect the critical section. The function in Program 14.3 is meant to be used as a thread. It outputs a message, one character at a time. To make it more likely to be interrupted in the middle of the message, the thread sleeps for 10 ms after each character is output. Program 14.4 creates a number of threadout threads and waits for them to terminate.

Program 14.3 threadcritical.c

A thread with an unprotected critical section .

 #include <pthread.h> #include <stdio.h> #include <unistd.h> #define BUFSIZE 1024 #define TEN_MILLION 10000000L /* ARGSUSED */ void *threadout(void *args) {     char buffer[BUFSIZE];     char *c;     struct timespec sleeptime;     sleeptime.tv_sec = 0;     sleeptime.tv_nsec = TEN_MILLION;     snprintf(buffer, BUFSIZE, "This is a thread from process %ld\n",              (long)getpid());     c = buffer;    /*****************start of critical section ********************/     while (*c != ' 
 #include <pthread.h> #include <stdio.h> #include <unistd.h> #define BUFSIZE 1024 #define TEN_MILLION 10000000L /* ARGSUSED */ void *threadout(void *args) { char buffer[BUFSIZE]; char *c; struct timespec sleeptime; sleeptime.tv_sec = 0; sleeptime.tv_nsec = TEN_MILLION; snprintf (buffer, BUFSIZE, "This is a thread from process %ld\n", (long)getpid()); c = buffer; /*****************start of critical section ********************/ while (*c != '\0') { fputc (*c, stderr); c++; nanosleep (&sleeptime, NULL); } /*******************end of critical section ********************/ return NULL; } 
') { fputc(*c, stderr); c++; nanosleep(&sleeptime, NULL); } /*******************end of critical section ********************/ return NULL; }
Exercise 14.16

What would happen if Program 14.4 were run with four threads?

Answer:

Most likely each thread would print the first character of its message, and then each would print the second character of its message, etc. All four messages would appear on one line followed by four newline characters .

Exercise 14.17

Why did we use nanosleep instead of a busy-waiting loop as in Program 14.1?

Answer:

Some thread-scheduling algorithms allow a busy-waiting thread to exclude other threads of the same process from executing.

Exercise 14.18

Why didn't we have the thread in Program 14.3 print its thread ID?

Answer:

The thread ID is of type pthread_t . Although many systems implement this as an integral type that can be cast to an int and printed, the standard does not require that pthread_t be of integral type. It may be a structure.

Program 14.4 maincritical.c

A main program that creates a number of threads .

 #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> void *threadout(void *args); int main(int argc, char *argv[]) {     int error;     int i;     int n;     pthread_t *tids;     if (argc != 2){   /* check for valid number of command-line arguments */         fprintf (stderr, "Usage: %s numthreads\n", argv[0]);         return 1;     }     n = atoi(argv[1]);     tids = (pthread_t *)calloc(n, sizeof(pthread_t));     if (tids == NULL) {         perror("Failed to allocate memory for thread IDs");         return 1;     }     for (i = 0; i < n; i++)         if (error = pthread_create(tids+i, NULL, threadout, NULL)) {             fprintf(stderr, "Failed to create thread:%s\n", strerror(error));             return 1;         }     for (i = 0; i < n; i++)         if (error = pthread_join(tids[i], NULL)) {             fprintf(stderr, "Failed to join thread:%s\n", strerror(error));             return 1;         }     return 0; } 

Program 14.5 is a version of Program 14.3 that protects its critical section by using a semaphore passed as its parameter. Although the main program does not use signals, this program restarts sem_wait if interrupted by a signal to demonstrate how to use semaphores with signals. Program 14.6 shows the corresponding main program. The main program initializes the semaphore to 1 before any of the threads are created.

Program 14.5 threadcriticalsem.c

A thread with a critical section protected by a semaphore passed as its parameter .

 #include <errno.h> #include <pthread.h> #include <semaphore.h> #include <stdio.h> #include <unistd.h> #define TEN_MILLION 10000000L #define BUFSIZE 1024 void *threadout(void *args) {     char buffer[BUFSIZE];     char *c;     sem_t *semlockp;     struct timespec sleeptime;     semlockp = (sem_t *)args;     sleeptime.tv_sec = 0;     sleeptime.tv_nsec = TEN_MILLION;     snprintf(buffer, BUFSIZE, "This is a thread from process %ld\n",              (long)getpid());     c = buffer;    /****************** entry section *******************************/     while (sem_wait(semlockp) == -1)        /* Entry section */         if(errno != EINTR) {             fprintf(stderr, "Thread failed to lock semaphore\n");             return NULL;         }    /****************** start of critical section *******************/     while (*c != ' 
 #include <errno.h> #include <pthread.h> #include <semaphore.h> #include <stdio.h> #include <unistd.h> #define TEN_MILLION 10000000L #define BUFSIZE 1024 void *threadout(void *args) { char buffer[BUFSIZE]; char *c; sem_t *semlockp; struct timespec sleeptime; semlockp = (sem_t *)args; sleeptime.tv_sec = 0; sleeptime.tv_nsec = TEN_MILLION; snprintf(buffer, BUFSIZE, "This is a thread from process %ld\n", (long)getpid()); c = buffer; /****************** entry section *******************************/ while (sem_wait(semlockp) == -1) /* Entry section */ if(errno != EINTR) { fprintf(stderr, "Thread failed to lock semaphore\n"); return NULL; } /****************** start of critical section *******************/ while (*c != '\0') { fputc(*c, stderr); c++; nanosleep(&sleeptime, NULL); } /****************** exit section ********************************/ if (sem_post(semlockp) == -1) /* Exit section */ fprintf(stderr, "Thread failed to unlock semaphore\n"); /****************** remainder section ***************************/ return NULL; } 
') { fputc(*c, stderr); c++; nanosleep(&sleeptime, NULL); } /****************** exit section ********************************/ if (sem_post(semlockp) == -1) /* Exit section */ fprintf(stderr, "Thread failed to unlock semaphore\n"); /****************** remainder section ***************************/ return NULL; }
Exercise 14.19

What happens if you replace the following line of Program 14.6

 semlock = sem_init(*semlock, 0, 1) 

with the following?

 semlock = sem_init(*semlock, 0, 0) 

Answer:

The original sem_init sets the initial value of semlock to 1, which allows the first process to successfully acquire the semaphore lock when it executes sem_wait . The replacement sets the initial value of semlock to 0, causing a deadlock. All of the processes block indefinitely on sem_wait .

Program 14.6 maincriticalsem.c

A main program that creates a semaphore and passes it to a number of threads .

 #include <pthread.h> #include <semaphore.h> #include <stdio.h> #include <stdlib.h> #include <string.h> void *threadout(void *args); int main(int argc, char *argv[]) {     int error;     int i;     int n;     sem_t semlock;     pthread_t *tids;     if (argc != 2){   /* check for valid number of command-line arguments */         fprintf (stderr, "Usage: %s numthreads\n", argv[0]);         return 1;     }     n = atoi(argv[1]);     tids = (pthread_t *)calloc(n, sizeof(pthread_t));     if (tids == NULL) {         perror("Failed to allocate memory for thread IDs");         return 1;     }     if (sem_init(&semlock, 0, 1) == -1) {         perror("Failed to initialize semaphore");         return 1;     }     for (i = 0; i < n; i++)         if (error = pthread_create(tids + i, NULL, threadout, &semlock)) {             fprintf(stderr, "Failed to create thread:%s\n", strerror(error));             return 1;         }     for (i = 0; i < n; i++)         if (error = pthread_join(tids[i], NULL)) {             fprintf(stderr, "Failed to join thread:%s\n", strerror(error));             return 1;         }     return 0; } 

Exercise 14.19 illustrates the importance of properly initializing the semaphore value. The sem_getvalue function allows a user to examine the value of either a named or unnamed semaphore. This function sets the integer referenced by sval to the value of the semaphore without affecting the state of the semaphore. Interpretation of sval is a little tricky: It holds the value that the semaphore had at some unspecified time during the call, but not necessarily the value at the time of return. If the semaphore is locked, sem_getvalue either sets sval to zero or to a negative value indicating the number of threads waiting for the semaphore at some unspecified time during the call.

  SYNOPSIS  #include <semaphore.h>     int sem_getvalue(sem_t *restrict sem, int *restrict sval);  POSIX:SEM  

If successful, sem_getvalue returns 0. If unsuccessful, sem_getvalue returns “1 and sets errno . The sem_getvalue function sets errno to EINVAL if *sem does not correspond to a valid semaphore.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net