
// PhilTicket.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* The Dining philosophers problem:
      https://en.wikipedia.org/wiki/Dining_philosophers_problem

  A ticket-based version to avoid deadlocks.

  The tickets have the tuple form:
     ("TICKET")
  which means that a philipospher thread can select any
  one in order to continue. It must add it back to the
  space when they have finished eating.

  Usage:
    > compile.bat PhilTicket.java

    Part of the application made up of:
       * DiningPhilTickets.java
       * PhilTicket.java

  For a version not using tickets, see Philosophers.java
  which is called by DiningPhilosophers.java
*/


import java.util.*;

import org.jspace.*;


public class PhilTicket implements Runnable
{
  private Random random = new Random();
  private PrintAreas pas;
  private Space table;
  private int id, leftFork, rightFork;

  private int eatenCount = 0;


  public PhilTicket(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;
    rightFork = right;
  }  // end of PhilTicket()


  public void run()
  {
    try {
      while (true) {
        int sleepTime = random.nextInt(1000);
        pas.println(id, "Thinking for " + sleepTime + "ms");
        SpaceUtils.pause(sleepTime);

        pas.println(id, "Waiting for a ticket");
        table.get( new ActualField("TICKET"));
        pas.println(id, "Got ticket");

        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);

        table.put("TICKET");
        pas.println(id, "Released ticket");
      }
    }
    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 PhilTicket class

