
//created by Dr. Ramsey Fall 2009

#include <semaphore.h>
#include <pthread.h>

#include <iostream>
#include <time.h>
using namespace std;


//how many phil?
#define N 5
// a timetoeat of 5 means phil eats for 0-4 seconds
//a timetoeat of 1 means a phil eats for 0 seconds
#define TIMETOEAT N
#define TIMETOTHINK N





//the mutexes for chopsticks
pthread_mutex_t c[N];

//attributes of the mutexes
pthread_mutexattr_t mutexattr;

//array of thread ids
pthread_t tid[N];

//state of the phil
int state[N];

//enums for the states of the phil
#define HUNGRY 0
#define EATING 1
#define THINKING 2




void eat(int);
void think(int);
void *runner(void *param);







//main - set up threads, phil, etc and print out some status
int main()
{
  srand(time(NULL));

  
  //set up the mutexes for chopsticks.
  for(int i = 0; i < N; i++)
    {
      //set up the mutexes
      pthread_mutex_init(&c[i], NULL);
      state[i] = THINKING; // initial state of the phil
    }

  pthread_attr_t attr;
  pthread_attr_init(&attr); //attributes for the threads

  char buf[N][16]; //buf for phil identifier
  //this loop creates the threads and runs them
  for(int i = 0; i < N; i++)
    {
      sprintf(buf[i],"%d",i); //set up a char array to identify phil i
      pthread_create(&tid[i],&attr,runner,buf[i]); //create and run the threads
    }

  while(true)
    {
      sleep(1);
      //print out some status of the phil every second
      cout << "******************************" << endl;
      for(int i = 0; i < N; i++)
	{
	  cout << "Philosopher " << i << ": ";
	  if(state[i] == HUNGRY)
	    cout << "HUNGRY";
	  else if (state[i] == EATING)
	    cout << "EATING";
	  else if (state[i] == THINKING)
	    cout << "THINKING";
	  cout << endl;
	}
      cout << "******************************" << endl;
    }


  //we should never get here because while(true) is always true
  for(int i = 0; i < N; i++)
    pthread_join(tid[i],NULL);

  //we can never get here because the threads never exit and thus
  //we can never join them all
}



//which philosopher is eating? phil i.
// he needs fork i and fork i+1 or rather (i+1)%N
void eat(int i)
{
  int j = (i+1)%N;
  state[i] = HUNGRY; // I'm hungry now!
  cout << "(*) Philosopher " << i << " wants to eat " << endl;

  //your code here
  //get chopsticks here


  state[i] = EATING; // I have my chopsticks - I'm gonna eat!
  cout << "(*) Philosopher " << i << " is eating " << endl;
  int v = rand()%TIMETOEAT;
  sleep(v);

  //your code here
  //give up chopsticks here

  //think() function will fix my state to thinking
  cout << "(*) Philosopher " << i << " is done eating " << endl;

}



//we think for 0 to TIMETOTHINK seconds and then we get hungry
void think(int i)
{
  state[i] = THINKING; 
  cout << "(*) Philosopher " << i << " is thinking " << endl;
  int v = rand()%TIMETOTHINK;
  sleep(v);
  cout << "(*) Philosopher " << i << " is done thinking " << endl;
}





//this is what phil do. They eat and think in a loop forever
void *runner(void *param)
{
  int i = atoi((char *)param);
  while(true)
    {
      think(i);
      eat(i);
    }
}
