Philosophers:

The philosopher struct (or class) contains information regarding the current state of each philosophers. The three states are thinking, hungry, and eating. A user-defined enumeration may be used.

enum Action {
  THINKING,
  HUNGRY,
  EATING,
};

 

Resource Sharing:

Furthermore, each philosopher will have a binary semaphore. In the case where the philosopher is hungry but unsuccessful in acquiring the forks, the semaphore will block until the forks are available. The CSemaphore class may be used for implementing the semaphores.

To ensure that no deadlock will occurs, each philosopher will make sure that both forks are available before taking either one of them. By using a mutex, only one philosopher may try to acquire the forks and start eating at one time. The CMutex class may be used for implementing the mutex.

 

Thinking & Eating:

The main process will be alternating the philosophers between eating and thinking. More specifically, the sequence should be thinking, taking forks, eating, putting down forks, and repeat. For this simulation, the Win32 function sleep() will be used for the sleeping and eating processes. Each philosopher will eat and think for a random amount of time.

 

Take Forks & Put Forks:

Basically, before taking the forks, a philosopher will enter a critical region and see if both forks are available for taking. If both forks are free, the philosopher will enter the EATING state and exit the critical region. If the forks are unavailable at the moment, the philosopher will remain HUNGRY, exit the critical region, and block. Upon replacing the fork, the philosopher will return to THINKING while freeing the forks for the neighbours within a critical region.

int number = 5;

void PutForks(int current) {
  mutex.Lock();
  philosopher[current].state = THINKING;
  SendMessage(STATE_CHANGE);
  Test((current+number-1)%number);
  Test((current+1)%number);
  mutex.Unlock();
}

void TakeForks(int current) {
  mutex.Lock();
  philosopher[current].state = HUNGRY;
  SendMessage(STATE_CHANGE);
  Test(current);
  mutex.Unlock();
  philosopher[current].semaphore->Lock();
}

void Test(int current) {
  if ((philosopher[current].state == HUNGRY)&&
    (philosopher[(current+number-1)%number].state != EATING)&&
    (philosopher[(current+1)%number].state != EATING)) {
    philosopher[current].state = EATING;
    SendMessage(STATE_CHANGE);
    philosopher[current].semaphore->Unlock();
  }
}

 

Threads:

Each philosopher is controlled by a worker thread. The AfxBeginThread() function may be used for starting the threads, one function call per thread.

AfxBeginThread(ThreadProc, reinterpret_cast<LPVOID>(current));

The thread functions must be a static function with a return type UINT: static UINT ThreadProc(LPVOID p);

UINT ThreadProc(LPVOID p) {
  int current=reinterpret_cast<int>(p);
  while (is_simulating) {
    Think();
    TakeForks(current);
    Eat();
    PutForks(current);
  }
  return 0;
}

For communications between the UI thread and the worker threads (for the changing of a philosopher's state), the PostMessage() or SendMessage() function may be used. Upon receiving the messages from the worker threads, the UI thread will update the graphics on the output screen accordingly.

The thread will end with the ThreadProc() function.

 

Drawing the Plates and Forks (Chopsticks):

The coordinates for the plates and forks may be calculated with simple trigonometric functions from <math.h>. The necessary data are the center point of the window, the radius (distance from the center point to the plate), and the degree of the plate with respect to north. For example, if there are 5 plates, the degree for each plate will be 0, 0.4 p, 0.8 p, 1.2 p, and 1.6 p. For translating from polar coordinates to Cartesian coordinates, the sin() and cos() math function may be used.

Similar techniques may be applied for calculating the coordinates of the forks.