
// Philosopher.java
// Michele Loreti and the jSpace Developers, 2017
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* The Dining philosophers problem:
      https://en.wikipedia.org/wiki/Dining_philosophers_problem

   Deadlock is avoided by having the last philosopher pick
   up his forks in a different order than the others.
 
   For example, Phil-0 will pick up fork-0 and then fork-1
                Phil-1 will pick up fork-1 and then fork-2
                 ...
   but Phil-4 will pick up fork-0 and then fork-4.

   This is coded using Math.min(). If this trick isn't used then
   deadlock will always occur since Phil-4 will go for fork-4
   first and then try for fork-0.

  Usage:
    > compile.bat Philosopher.java

    Part of the application made up of:
       * DiningPhilosophers.java
       * Philosopher.java
*/

import java.util.*;

import org.jspace.*;


public class Philosopher implements Runnable
{
  private Random random = new Random();
  private PrintAreas pas;
  private Space table;
  private int id, leftFork, rightFork;

  private int eatenCount = 0;


  public Philosopher(int id, PrintAreas pas, Space s, 
                                     int left, int right)
  {
    table = s;
    this.id = id;
    this.pas = pas;
    pas.setTitle(id, "PHIL-"+id);

    //leftFork = left;   // causes deadlock
    // rightFork = right;

    leftFork = Math.min(left, right);   // avoids deadlock
    rightFork = Math.max(left, right);
  }  // end of Philosopher()


  public void run()
  {
    try {
      while (true) {
        int sleepTime = random.nextInt(1000);
        pas.println(id, "Thinking for " + sleepTime + "ms");
        SpaceUtils.pause(sleepTime);

        getFork(leftFork);
        getFork(rightFork);

        sleepTime = random.nextInt(1000);
        pas.println(id, "EATING with forks " + leftFork + " and " +
                        rightFork + " for " + sleepTime + "ms");
        SpaceUtils.pause(sleepTime);

        eatenCount++;
        pas.println(id, "** Eaten " + eatenCount + " times");

        releaseFork(leftFork);
        releaseFork(rightFork);
      }
    }
    catch (InterruptedException e) 
    {  pas.println(id, "Error: " + e);  }
  } /* run */



  private void getFork(int i) throws InterruptedException
  {
    pas.println(id, "Waiting for fork " + i);
    table.get(new ActualField("FORK"), new ActualField(i));
    pas.println(id, "Got fork " + i);
  }


  private void releaseFork(int i) throws InterruptedException
  {
    // pas.println(id, "Releasing fork " + i);
    table.put("FORK", i);
    pas.println(id, "Released fork "+ i);
  } 

}  // end of Philosopher class
