/* chopsticks2.c chopstick management routines for Dining Philosophers example using processes This version uses sigsuspend instead of usleep to wait when the lock is held. It prevents deadlock by using ordered resource allocation but does not contain any code to prevent starvation. */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include "chopsticks.h" #define LOCKFILENAME_LENGTH 8 #define MYSIG SIGUSR1 /* SIGUSR1 is always available for application use */ #define CHECK(CALL)\ if ((CALL) < 0) {\ perror (#CALL);\ chopsticks_emergency_stop();\ } sigset_t just_mysig; sigset_t unmask_mysig; char * lockfilename[NTHREADS]; int lockfd[NTHREADS]; pid_t maybe_waiting[NTHREADS]; void mysig_handler (int sig) { } void lock (int i) { int fildes; while ((fildes = open (lockfilename[i], O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { if (errno != EEXIST) chopsticks_emergency_stop(); /* wait to try again until we get a signal */ sigsuspend (&unmask_mysig); if (errno != EINTR) chopsticks_emergency_stop(); } CHECK (close (fildes)); } void unlock (int i) { CHECK (unlink (lockfilename[i]) == -1); } void block_mysig () { CHECK (sigprocmask (SIG_BLOCK, &just_mysig, NULL)); } void unblock_mysig () { CHECK (sigprocmask (SIG_UNBLOCK, &just_mysig, NULL)); } void chopsticks_init() { int i; struct sigaction act; /* fetch old treatment for MYSIG */ CHECK (sigaction (MYSIG, NULL, &act)); /* modify treatment of MYSIG to use our handler */ act.sa_handler = mysig_handler; CHECK (sigaction (MYSIG, &act, NULL)); /* create signal mask with just MYSIG */ CHECK (sigemptyset (&just_mysig)); CHECK (sigaddset (&just_mysig, MYSIG)); /* create signal mask with MYSIG removed */ CHECK (sigprocmask (SIG_SETMASK, NULL, &unmask_mysig)); CHECK (sigdelset (&unmask_mysig, MYSIG)); for (i = 0; i < NTHREADS; i++) { lockfilename[i] = (char *) malloc (LOCKFILENAME_LENGTH); memset ((void *) lockfilename[i], 0, LOCKFILENAME_LENGTH); sprintf (lockfilename[i], "LCK%d5", i); maybe_waiting[i] = 0; } } void chopsticks_take (int i) { block_mysig(); maybe_waiting[i] = getpid(); lock (i); if (i == (NTHREADS - 1)) { lock (0); lock (NTHREADS - 1); } else { lock (i); lock ((i + 1) % NTHREADS); } maybe_waiting[i] = 0; unblock_mysig(); } void chopsticks_put (int i) { unlock (i); unlock ((i + 1) % NTHREADS); /* wake up our neighbors if they might be waiting */ CHECK (kill (maybe_waiting[(i + 1 ) % NTHREADS], MYSIG)); if (i == 0) { CHECK (kill (maybe_waiting[NTHREADS - 1], MYSIG)); } else { CHECK (kill (maybe_waiting[i - 1], MYSIG)); } } void chopsticks_finalize () { int i; for (i = 0; i < NTHREADS; i++) { unlink (lockfilename[i]); /* ignore result intentionally */ } } void chopsticks_emergency_stop() { fprintf (stderr, "*** EMERGENCY STOP***\n"); chopsticks_finalize (); /* kill self and all other members of the current process group */ CHECK (kill (0, SIGKILL)); fprintf (stderr, "should never get here\n"); exit (-1); } /* It might be instructive to mention some errors I made when I first wrote this code. 1) I inverted the order of the arguments to kill(), putting the process ID value in the place of the signal value, and vice versa. This is typical of the kind of programming errors the compiler might catch in another language, that are not caught by the C compiler. The C compiler cannot catch this, since both process IDs and signals are represented by integer values. The effect of this error was that signals were not generated for the right processes, and so the processes never woke up from their calls to sigsuspend. 2) I used the operator % as if it computed the mathematical "mod" operation, not remembering that it really is the remainder operation. For example, (-1) % 5 = -1, whereas (-1) mod 5 = 4. Thus, we cannot use the operator % when we want to move around a circular buffer or table in the reverse direction. */