import Utilities.*;

class BoundedBuffer extends MyObject {  // designed for multiple producer
                                    // threads and multiple consumer threads
   private int numSlots = 0;
   private double[] buffer = null;
   private int putIn = 0, takeOut = 0;
   private int elements = 0;
   private int spaces = 0;
/*
 * The signal-and-continue effect of barging is handled here by having
 * all producer threads block if there are no spaces until woken up one
 * at a time and similarly for consumer threads and elements.  Instead of
 * the state variables spaces and elements recording the state of the
 * bounded buffer, they record the demand for spaces and elements by the
 * producer and consumer threads.  Also only notify() is necessary, not
 * notifyAll().  This is about as close as we can get to named condition
 * variables in Java: using these "notification" objects.
 */
   private Object conveyD = null, conveyF = null;

   public BoundedBuffer(int numSlots) {
      super("BoundedBuffer, numSlots="+numSlots);
      if (numSlots <= 0) throw new IllegalArgumentException("numSlots<=0");
      this.numSlots = numSlots;
      buffer = new double[numSlots];
      for (int i = 0; i < numSlots; i++) buffer[i] = 0.0;
      elements = 0;
      spaces = numSlots;
      conveyD = new Object();
      conveyF = new Object();
      System.out.println(this + " is alive");
   }

   public void deposit(double value) {
      synchronized (conveyD) { // grab a space or wait behind others
         spaces--;
         if (spaces < 0) {
            while (true) {
               try {
                  conveyD.wait();
                  break;                           // notified so ok
               } catch (InterruptedException e) {
                  // handle notify() after interrupt() race condition
                  if (spaces >= 0) break; // notify() after interrupt()
                  else continue;      // interrupted so continue waiting
               }
            }
         }
         buffer[putIn] = value;
         putIn = (putIn + 1) % numSlots;
      }
      synchronized (conveyF) { // signal a waiting consumer if there is one
         elements++;
         if (elements <= 0) conveyF.notify();
      }
   }

   public double fetch() {
      double value;
      synchronized (conveyF) { // grab an element or wait behind others
         elements--;
         if (elements < 0) {
            while (true) {
               try {
                  conveyF.wait();
                  break;                            // notified so ok
               } catch (InterruptedException e) {
                  // handle notify() after interrupt() race condition
                  if (elements >= 0) break; // notify() after interrupt()
                  else continue;      // interrupted so continue waiting
               }
            }
         }
         value = buffer[takeOut];
         takeOut = (takeOut + 1) % numSlots;
      }
      synchronized (conveyD) { // signal a waiting producer if there is one
         spaces++;
         if (spaces <= 0) conveyD.notify();
      }
      return value;
   }

   public String toString() { // should be synchronized on conveyD,R
      return getName() + ", spaces=" + spaces + ", elements=" + elements;
   }
}

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

D:\>javac bbcv.java bbml.java

D:\>java ProducersConsumers -R10
ProducersConsumers:
 numSlots=20, numProducers=2, numConsumers=3, pNap=3, cNap=2, runTime=10
BoundedBuffer, numSlots=20, spaces=20, elements=0 is alive
age=50, PRODUCER0 napping for 2594 ms
age=50, PRODUCER1 napping for 1774 ms
All threads started
age=110, Consumer1 napping for 548 ms
age=110, Consumer0 napping for 1740 ms
age=110, Consumer2 napping for 898 ms
age=660, Consumer1 wants to consume
age=1040, Consumer2 wants to consume
age=1870, PRODUCER1 produced item 0.86839
age=1870, PRODUCER1 deposited item 0.86839
age=1870, PRODUCER1 napping for 989 ms
age=1870, Consumer0 wants to consume
age=1870, Consumer1 fetched item 0.86839
age=1870, Consumer1 napping for 1711 ms
age=2690, PRODUCER0 produced item 0.507812
age=2690, PRODUCER0 deposited item 0.507812
age=2690, PRODUCER0 napping for 1008 ms
age=2690, Consumer2 fetched item 0.507812
age=2690, Consumer2 napping for 1038 ms
age=2850, PRODUCER1 produced item 0.768203
age=2850, PRODUCER1 deposited item 0.768203
age=2850, PRODUCER1 napping for 1366 ms
age=2850, Consumer0 fetched item 0.768203
age=2850, Consumer0 napping for 1460 ms
age=3620, Consumer1 wants to consume
age=3680, PRODUCER0 produced item 0.0275597
age=3680, PRODUCER0 deposited item 0.0275597
age=3680, PRODUCER0 napping for 59 ms
age=3680, Consumer1 fetched item 0.0275597
age=3680, Consumer1 napping for 741 ms
age=3730, Consumer2 wants to consume
age=3730, PRODUCER0 produced item 0.65506
age=3730, PRODUCER0 deposited item 0.65506
age=3730, PRODUCER0 napping for 456 ms
age=3730, Consumer2 fetched item 0.65506
age=3730, Consumer2 napping for 1899 ms
age=4230, PRODUCER0 produced item 0.201681
age=4230, PRODUCER0 deposited item 0.201681
age=4230, PRODUCER0 napping for 2697 ms
age=4230, PRODUCER1 produced item 0.739107
age=4230, PRODUCER1 deposited item 0.739107
age=4230, PRODUCER1 napping for 748 ms
age=4340, Consumer0 wants to consume
age=4340, Consumer0 fetched item 0.201681
age=4340, Consumer0 napping for 385 ms
age=4450, Consumer1 wants to consume
age=4450, Consumer1 fetched item 0.739107
age=4450, Consumer1 napping for 1293 ms
age=4720, Consumer0 wants to consume
age=5000, PRODUCER1 produced item 0.437976
age=5000, PRODUCER1 deposited item 0.437976
age=5000, PRODUCER1 napping for 2489 ms
age=5000, Consumer0 fetched item 0.437976
age=5000, Consumer0 napping for 714 ms
age=5650, Consumer2 wants to consume
age=5710, Consumer0 wants to consume
age=5760, Consumer1 wants to consume
age=6920, PRODUCER0 produced item 0.444391
age=6920, PRODUCER0 deposited item 0.444391
age=6920, PRODUCER0 napping for 2621 ms
age=6920, Consumer2 fetched item 0.444391
age=6920, Consumer2 napping for 1248 ms
age=7470, PRODUCER1 produced item 0.92733
age=7470, PRODUCER1 deposited item 0.92733
age=7470, PRODUCER1 napping for 1145 ms
age=7470, Consumer0 fetched item 0.92733
age=7470, Consumer0 napping for 205 ms
age=7690, Consumer0 wants to consume
age=8180, Consumer2 wants to consume
age=8620, PRODUCER1 produced item 0.223514
age=8620, PRODUCER1 deposited item 0.223514
age=8620, PRODUCER1 napping for 933 ms
age=8620, Consumer1 fetched item 0.223514
age=8620, Consumer1 napping for 711 ms
age=9330, Consumer1 wants to consume
age=9550, PRODUCER0 produced item 0.946951
age=9550, PRODUCER0 deposited item 0.946951
age=9550, PRODUCER0 napping for 2683 ms
age=9550, Consumer0 fetched item 0.946951
age=9550, Consumer0 napping for 466 ms
age=9550, PRODUCER1 produced item 0.49459
age=9550, PRODUCER1 deposited item 0.49459
age=9550, PRODUCER1 napping for 351 ms
age=9550, Consumer2 fetched item 0.49459
age=9550, Consumer2 napping for 986 ms
age=9940, PRODUCER1 produced item 0.444163
age=9940, PRODUCER1 deposited item 0.444163
age=9940, PRODUCER1 napping for 2310 ms
age=9940, Consumer1 fetched item 0.444163
age=9940, Consumer1 napping for 643 ms
age=10050, Consumer0 wants to consume
age()=10100, time to stop the threads and exit
                                            ... end of example run(s)  */
