////////////////////////////////////////////////////////////////////////////////
//
// UFRGS - INF01151 - Marcelo Johann - 2009/1
//
// Producers and Consumers in Java with Semaphores
//
// The delay used at Buffer's insert and remove methods is implemented as a loop
// to allow loosing the CPU inside these points. The rest of the code uses the
// Java sleep procedure. You can test with other ways of spending time to check
// different synchings...

public class PnCn
  {
  public static void main(String args[])
    {
    Buffer b = new Buffer(20);
    Producer p1 = new Producer(b,'A',100);
    Producer p2 = new Producer(b,'B',100);
    Consumer c1 = new Consumer(b,'X',300);
    Consumer c2 = new Consumer(b,'Y',300);
    Consumer c3 = new Consumer(b,'Z',300);
    b.start();
    p1.start();
    p2.start();
    c1.start();
    c2.start();
    c3.start();
    }
  }

class Producer extends Thread
  {
  private char name;
  private int ticks;
  private Buffer buffer;
  public Producer( Buffer b, char id, int delay )
    {
    buffer = b;
    name = id;
    ticks = delay;
    }
  public void run()
    {
    Item it;
    for (int i=0; true; ++i)
      {
      try {sleep(ticks);} 
      catch (InterruptedException e){}
      System.err.println("Producer "+name+" to produce "+i);
      buffer.insert(name,i);
      }
    }
  }

class Consumer extends Thread
  {
  private char name;
  private int ticks;
  private Buffer buffer;
  public Consumer( Buffer b, char id, int delay )
    {
    buffer = b;
    name = id;
    ticks = delay;
    }
  public void run()
    {
    Item it;
    while (true)
      {
      try {sleep(ticks);} 
      catch (InterruptedException e){}
      it = buffer.remove();
      System.err.print("Consumer "+name+" got ");
      it.print();
      System.err.println("");
      }
    }
  }
    
class Item
  {
  public char owner;
  public int number;
  public Item(char ow,int num)
    {
    owner = ow;
    number = num;
    }
  public void print()
    {
    System.err.print(owner);
    System.err.print(number+"_");
    }
  }
  
class Semaphore 
  {
  int value;
  public  Semaphore(int initialValue)
    {
    value = initialValue;
    }
  public synchronized void P() 
    {
    if (value == 0 ) 
      {
      try { wait(); }
      catch(InterruptedException e){}
      }
    value--;
    }
  public synchronized void V() 
    {
    value++;
    notify();
    }
  }

  
class Buffer extends Thread
  {
  final int DELAY_BUFFER = 400;
  private Item[] buffer;
  private int bufferSize = 0;
  private int nextFree = 0;
  private int lastItem = 0;
  private Semaphore mutex;
  private Semaphore empty_places;
  private Semaphore available_items;
  public Buffer(int size)
    {
    bufferSize = size;
    buffer = new Item[size];
    Item nothing = new Item(' ',0);
    for (int i=0; i<size; ++i)
      buffer[i]= nothing;
    mutex = new Semaphore(1);
    empty_places = new Semaphore(size);
    available_items = new Semaphore(0);
    }
  public void run()
    {
    while (true)
      {
      print();
      try {sleep(50);} 
      catch (InterruptedException e){}
      }
    }
  public void insert(char c, int n)
    {
    Item fresh = new Item(c,n);
    empty_places.P();
    mutex.P();
    buffer[nextFree]=fresh;
    delay(DELAY_BUFFER);
    nextFree = (nextFree+1)%bufferSize;
    delay(DELAY_BUFFER);
    mutex.V();
    available_items.V();
    }
  public Item remove()
    {
    Item out;
    Item nothing = new Item(' ',0);
    available_items.P();
    mutex.P();
    out = buffer[lastItem];
    buffer[lastItem] = nothing;
    delay(DELAY_BUFFER);
    lastItem = (lastItem+1)%bufferSize;
    delay(DELAY_BUFFER);
    mutex.V();
    empty_places.V();
    return out;
    }
  public void print()
    {
    System.err.print("Buffer: ");
    for (int i=0; i<bufferSize; ++i)
      {
      Item it = buffer[i];
      it.print();
      }
    System.err.println("");
    }
  private void delay(int ticks)
    {
    int j,k;
    for (j=0;j<ticks;++j)
      {
      for (k=0;k<10000;++k)
        {
        j = j + 100;
        j = j - 100;
        }
      }
    }
  }
  
  


