import Utilities.*;
import Synchronization.*;

class CorruptedQueueException extends Exception {}

class QueueItem extends MyObject {

   protected double value = 0;
   protected QueueItem nextItem = null;

   protected QueueItem() { super(); }
   protected QueueItem(double value) { this.value = value; }
}

class UnboundedBuffer extends QueueItem { // can be used with multiple
                                          // producers and consumers
// extend QueueItem so that only this class can access value and nextItem
   private int count = 0;
   private CountingSemaphore elements = new CountingSemaphore(0);
   private BinarySemaphore mutex = new BinarySemaphore(1);
   private QueueItem head = null;
   private QueueItem tail = null;

   public UnboundedBuffer() {
      System.out.println("UnboundedBuffer alive");
   }

   public void deposit(double value) throws CorruptedQueueException {
      P(mutex);
      QueueItem oldTail = null;
      QueueItem newOne = new QueueItem(value);
      if (tail == null) { // add to an empty queue
         if (head != null) throw new CorruptedQueueException();
         else {
            head = newOne; tail = newOne;
         }
      } else {            // add to end of queue
         oldTail = tail; tail = newOne;
         oldTail.nextItem = newOne;
      }
      count++;
      System.out.println("deposit: count=" + count);
      V(mutex);
      V(elements);
   }

   public double fetch() throws CorruptedQueueException {
      double value;
      QueueItem oldOne = null;
      P(elements);
      P(mutex);
      if (head == null && tail == null)        // elements semaphore
         throw new CorruptedQueueException();  // should prevent this
      else if (head != null && tail != null) {
         if (head == tail) { // dequeue from a singleton queue
            oldOne = head;
            head = null; tail = null;
         } else {            // dequeue from beginning of queue
            oldOne = head;
            head = oldOne.nextItem;
         }
         count--;
         System.out.println("fetch: count=" + count);
      } else throw new CorruptedQueueException();
      value = oldOne.value;
      V(mutex);
      return value;
   }
}

/* ............... Example compile and run(s)

D:\>javac unbb.java bbml.java

D:\>java ProducersConsumers -P3 -C2 -c2 -p2 -R3
ProducersConsumers: numProducers=3, numConsumers=2, pNap=2, cNap=2, runTime=3
UnboundedBuffer alive
All threads started
age=110, PRODUCER 0 napping for 688 ms
age=160, Consumer 0 napping for 966 ms
age=110, PRODUCER 1 napping for 1523 ms
age=160, PRODUCER 2 napping for 832 ms
age=160, Consumer 1 napping for 1488 ms
age=880, PRODUCER 0 produced item 0.434521
deposit: count=1
PRODUCER 0 deposited item 0.434521
age=880, PRODUCER 0 napping for 1831 ms
age=1040, PRODUCER 2 produced item 0.696984
deposit: count=2
PRODUCER 2 deposited item 0.696984
age=1040, PRODUCER 2 napping for 143 ms
age=1150, Consumer 0 wants to consume
fetch: count=1
Consumer 0 fetched item 0.434521
age=1150, Consumer 0 napping for 1980 ms
age=1150, PRODUCER 2 produced item 0.0704563
deposit: count=2
PRODUCER 2 deposited item 0.0704563
age=1150, PRODUCER 2 napping for 1395 ms
age=1700, Consumer 1 wants to consume
fetch: count=1
Consumer 1 fetched item 0.696984
age=1700, Consumer 1 napping for 52 ms
age=1700, PRODUCER 1 produced item 0.233638
deposit: count=2
PRODUCER 1 deposited item 0.233638
age=1700, PRODUCER 1 napping for 1745 ms
age=1760, Consumer 1 wants to consume
fetch: count=1
Consumer 1 fetched item 0.0704563
age=1760, Consumer 1 napping for 1379 ms
age=2580, PRODUCER 2 produced item 0.978749
deposit: count=2
PRODUCER 2 deposited item 0.978749
age=2580, PRODUCER 2 napping for 856 ms
age=2690, PRODUCER 0 produced item 0.692208
deposit: count=3
PRODUCER 0 deposited item 0.692208
age=2690, PRODUCER 0 napping for 1812 ms
age=3130, Consumer 0 wants to consume
fetch: count=2
Consumer 0 fetched item 0.233638
age=3130, Consumer 0 napping for 1269 ms
age()=3130, time to stop the threads and exit

D:\>java ProducersConsumers -P2 -C3 -c2 -p2 -R3
ProducersConsumers: numProducers=2, numConsumers=3, pNap=2, cNap=2, runTime=3
UnboundedBuffer alive
All threads started
age=110, PRODUCER 0 napping for 667 ms
age=110, PRODUCER 1 napping for 1117 ms
age=110, Consumer 0 napping for 816 ms
age=110, Consumer 1 napping for 521 ms
age=110, Consumer 2 napping for 798 ms
age=660, Consumer 1 wants to consume
age=770, PRODUCER 0 produced item 0.934868
deposit: count=1
PRODUCER 0 deposited item 0.934868
age=770, PRODUCER 0 napping for 1479 ms
fetch: count=0
Consumer 1 fetched item 0.934868
age=770, Consumer 1 napping for 1291 ms
age=930, Consumer 2 wants to consume
age=930, Consumer 0 wants to consume
age=1260, PRODUCER 1 produced item 0.420986
deposit: count=1
PRODUCER 1 deposited item 0.420986
age=1260, PRODUCER 1 napping for 537 ms
fetch: count=0
Consumer 2 fetched item 0.420986
age=1260, Consumer 2 napping for 1386 ms
age=1810, PRODUCER 1 produced item 0.426525
deposit: count=1
PRODUCER 1 deposited item 0.426525
age=1810, PRODUCER 1 napping for 44 ms
fetch: count=0
Consumer 0 fetched item 0.426525
age=1810, Consumer 0 napping for 1598 ms
age=1860, PRODUCER 1 produced item 0.923759
deposit: count=1
PRODUCER 1 deposited item 0.923759
age=1860, PRODUCER 1 napping for 1787 ms
age=2080, Consumer 1 wants to consume
fetch: count=0
Consumer 1 fetched item 0.923759
age=2080, Consumer 1 napping for 895 ms
age=2250, PRODUCER 0 produced item 0.650406
deposit: count=1
PRODUCER 0 deposited item 0.650406
age=2250, PRODUCER 0 napping for 1478 ms
age=2630, Consumer 2 wants to consume
fetch: count=0
Consumer 2 fetched item 0.650406
age=2630, Consumer 2 napping for 48 ms
age=2690, Consumer 2 wants to consume
age=2960, Consumer 1 wants to consume
age()=3070, time to stop the threads and exit
                                            ... end of example run(s)  */
