#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

#define PHILO 5
#define DELAY 5000
#define FOOD 50

/* 
This demo prevents deadlock but not starvation.

There are 5 philosophers, numbered 0-4.
(They used to be computer scientists before they 
realized that all the money was in philosophy.)

The philosophers are seated in numerical order
clockwise around a round table.  The fork on the right 
has the same number as the philosopher's own number.
The fork on the left will have a larger number 
except for philosopher PHILO.

Right handed philosophers will reach for the fork
on their right first, then try to get the fork on the left.
In order for our scheme to
work right, philosopher #0 needs to be the only left handed
philospoher.

In other terms: always lock your locks in numerical order.
*/

pthread_mutex_t forks[PHILO];
pthread_t phils[PHILO];
void *philosopher (void *id);
int food_on_table();
int left_handed(int);
void get_fork(int, int, char *);
void down_fork(int, int);
pthread_mutex_t foodlock;

int main()
{
   int i;
   pthread_mutex_init(&foodlock,NULL);
   for (i=0; i<PHILO; i++)
	pthread_mutex_init(&forks[i],NULL);
   for (i=0; i<PHILO; i++)
	pthread_create (&phils[i], NULL, philosopher, (void *)i);
   for (i=0; i<PHILO; i++)
	pthread_join(phils[i], NULL);
   return 0;
}

void *philosopher (void *num)
{
    int id;
    int i;
    int left_fork;
    int right_fork;
    int spaghetti = 100;
    int f;

    id = (int)num;
    printf ("Philosopher %d sitting down to dinner.\n", id);
    right_fork = id;
    left_fork = id+1;
    /* Wrap around the forks. */
    if (left_fork == PHILO) left_fork = 0;
 
    /* verify that our left/right handed scheme produces
       the numerical ordering we need. */
    if (left_handed(id))  {
        assert(right_fork > left_fork);
    } else {
        assert(left_fork > right_fork);
    }
    /* Hence locks are locked from smaller to larger numbers. */

    while (f = food_on_table()) {
    	printf("Philosopher %d: eating dish %d.\n", id, f);
        if (left_handed(id)) {
	    get_fork(id, left_fork,  "left ");
	    get_fork(id, right_fork, "right");
        } else {
	    get_fork(id, right_fork, "right");
	    get_fork(id, left_fork,  "left ");
        }
        printf("Philosopher %d: eating.\n", id);
	usleep(DELAY * (FOOD-f+1));
	down_fork(left_fork, right_fork);
    }
    printf ("Philosopher %d is done eating.\n", id);
    return (NULL);
}

int food = FOOD;
int food_on_table() {
   pthread_mutex_lock(&foodlock);
   if (food>0) {
      food--;
   }
   pthread_mutex_unlock(&foodlock);
   return food;
}

int left_handed(int id)
{
   /* Only the last philosopher should be left handed. */
   /* If PHILO==5 then the number of the last one is 4. */
   if (id == PHILO-1)
      return 1;
   return 0;
}

void get_fork(int phil, int fork, char * hand)
{
    pthread_mutex_lock(&forks[fork]);
    printf("Philosopher %d: got %s fork %d\n",phil,hand,fork);
}

void down_fork(int f1, int f2)
{
    pthread_mutex_unlock(&forks[f1]);
    pthread_mutex_unlock(&forks[f2]);
}
