#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#define N               5       /* number of philosophers */
#define BUSY_EATING     1
#define BUSY_THINKING   1
#define Left(p)         (p)
#define Right(p)        (((p) + 1) % N)

typedef int * Semaphore;

Semaphore chopstick[N];

/* */
Semaphore make_semaphore(void)
{
  int * sema = calloc(2, sizeof(int));
  pipe(sema);
  return sema;
}

/* Wait on the semaphore s */
void halt(Semaphore s)
{
  int junk;
  if (read(s[0], &junk, 1) <= 0) {
    printf("ERROR: halt() failed, check semaphore creation!\n");
    exit(1);
  }
}

/* resume - Signals the semaphore s */
void resume(Semaphore s)
{
  if (write(s[1], "x", 1) <= 0) {
    printf("ERROR: resume() failed, check semaphore creation!\n");
    exit(1);    
  }
}

/* acquire chopstick, input is philosopher number */
void pick_up(int me)
{
  if (me == 0) {
    halt(chopstick[Right(me)]);
    printf("Philosopher %d picks up right chopstick\n", me);
    sleep(1);                   /* slow pick up -> encourage deadlock */
    halt(chopstick[Left(me)]);
    printf("Philosopher %d picks up left chopstick\n", me);
  } else {
    halt(chopstick[Left(me)]);
    printf("Philosopher %d picks up left chopstick\n", me);
    sleep(1);                   /* slow pick up -> encourage deadlock */ 
    halt(chopstick[Right(me)]);
    printf("Philosopher %d picks up right chopstick\n", me);
 }
}

/* Relinquish chopstick, input is a philosopher number */
void put_down(int me)
{
  resume(chopstick[Left(me)]);
  resume(chopstick[Right(me)]);
}

/* Philosopher process, input is a philosopher number */
void philosopher(int me)
{
  char * s;
  int i;

  for (i = 1; ; ++i) {
    pick_up(me);
    s = i == 1 ? "st" : i == 2 ? "nd" : i == 3 ? "rd" : "th";
    printf("%s%d%s%d%s%s\n",
           "Philosopher ", me, " eating for the ", i ,s, " time");
    sleep(BUSY_EATING);
    put_down(me);
    printf("Philosopher %d thinking\n", me);
    sleep(BUSY_THINKING);
  }
}

int main(int argc, char * argv[])
{
  int i;
  
  for (i = 0; i < N; ++i) {     /* put chopsticks on the table */
    chopstick[i] = make_semaphore();
    resume(chopstick[i]);
  }
  for (i = 0; i < N - 1; ++i)       /* crate philosophers */
    if (fork() == 0)
      break;
  philosopher(i);
  return 0;
}
