/* 
 * BoundedBufferMon.java (Implements Producer and Consumer Using 
 *                        Monitors).
 *
 * Written by : Nitin  Motgi (nmotgi@cs.ucf.edu)
 *
 * This file implements producer using Monitors. By using this kind of
 * construct, a collection of procedures is declared to be in a monitor 
 * and mutual exclusion is enforced on those procedures - only one 
 * process can be executing with it's program counter in the montiro code. 
 * Underneath the covers, this mutual exclusion is implemented with
 * semaphores, but all the calls to monitor routine are replaced by the
 * P() and V() operations by the compiler using 2nd Pass over the program
 * code.
 *
 * Sometimes a critical section will need to wait for another process to 
 * do work, like a consumer encountering an empty queue. Monitors need a
 * blocking mechanism, which is provided by condition variables.
 * 
 * Portions copyright(c) 2000 to School of Electrical Engineering and 
 * Computer Science, UCF, Orlando.                   
 *
 * Use and distribution of this source code are strictly governed by 
 * terms and conditions set by the authors.
 * 
 * $Id : BoundedBufferMon.java, v1.0.0 02/07/2001. $
 *
 * Revision History Revisited:
*/

import java.util.*;
import java.lang.*;

/* Start of BoundedBufferMon.*/
public class BoundedBufferMon{
     int nNumberOfProducers;            /* Tracks # of Producers.*/
     int nNumberOfConsumers;            /* Tracks # of Consumers.*/
     int nNumberOfSlots;                /* Number of Free Slots in Buffer */
     boolean bEndCondition;             /* End Condition for it.*/
     int nEvents;                       /* Basically # of items to be 
                                           produced.*/

     long lStartTime,lStopTime;         /* Timers used to print messages
                                           during simulation.*/
     int nItem;
     int nNumberOfItemsProduced;        /* Tracks Events.*/
     int nNumberOfItemsConsumed;        /* Tracks Completed Events.*/

     /* Synchronization Mechanism.*/
     Semaphore Empty;                   /* Used to check buffer empty.*/
     Semaphore Full;                    /* Trigger when buffer full.*/
     Semaphore Block;                   /* Block Consumer piece of code.*/
     Semaphore WBlock;

     BoundedBufferPMon Producer[];      /* Array of Producers.*/
     BoundedBufferCMon Consumer[];      /* Array of Consumers.*/

     
     /* 
      * This function is constructor for this class.
      */
     public BoundedBufferMon(int nNumberOfProducer,
                             int nNumberOfConsumer,
                             int nNumberOfSlots,
                             int nEvents,
                             long lStartTime){

       /* Store them within this P/C controller.*/
       this.nNumberOfProducers = nNumberOfProducer;
       this.nNumberOfConsumers = nNumberOfConsumer;
       this.nNumberOfSlots    = nNumberOfSlots;
       this.nEvents           = nEvents;  
       this.lStartTime        = lStartTime;   

       /* Initialise Synchronization Mechanism.*/
       Empty  = new Semaphore(1);
       Full   = new Semaphore(0);
       Block  = new Semaphore(1);
       WBlock = new Semaphore(1);

       /* Initialise Items.*/
       nItem = 0;
       nNumberOfItemsProduced = 0;
       nNumberOfItemsConsumed = 0;

     }/* End of BoundedBufferSem.*/

     /*
      * This program is heart of this                            
      */
      public void Start(){
        /* Create all the producers and consumers.*/
        Producer = new BoundedBufferPMon[nNumberOfProducers];
        Consumer = new BoundedBufferCMon[nNumberOfConsumers];

        /* Print the Start Time.*/
        long lInterStartTime = System.currentTimeMillis();
        TimeFormat Time = new TimeFormat();
        System.out.println("Started using Monitors at time " + 
                           Time.formatTime(lInterStartTime - lStartTime));
                           
        /* Initialise their Environment.*/
        for(int nIndex=0; nIndex < nNumberOfProducers; nIndex++){
         Producer[nIndex] = new BoundedBufferPMon(nIndex+1,this);
         Producer[nIndex].start();
        }/* End for.*/


        for(int nIndex=0; nIndex < nNumberOfConsumers; nIndex++){
         Consumer[nIndex] = new BoundedBufferCMon(nIndex+1,this);
         Consumer[nIndex].start();
        }/* End For.*/
        
        boolean bAllDead = false;
        while(bAllDead == false){
         bAllDead = true;
         for(int nIndex=0; nIndex < nNumberOfProducers; nIndex++){
          if(Producer[nIndex].isAlive() == true){ 
           bAllDead = false;
           break;
          }/* End if.*/
         }/* End for.*/

         for(int nIndex=0; nIndex < nNumberOfConsumers; nIndex++){
          if(Consumer[nIndex].isAlive() == true){ 
           bAllDead = false;
           break;
          }/* End if.*/
         }/* End for.*/
        }/* End While.*/

        lStopTime = System.currentTimeMillis();
        System.out.println("Completed using Monitors at time " + 
                           Time.formatTime(lStopTime - lStartTime));
        
      }/* End of Start.*/

      /*
       * Inserts an item in the Shared memory location. This shared
       * location is accessible by all the producers and consumers 
       * in the system.
       */
      public void Insert(int nSimItem,BoundedBufferPMon pThis){
       Block.P();
       if(nNumberOfItemsProduced >= nEvents){
        pThis.SetEndCondition(false);        
        Block.V();
        return;
       }/* End if.*/

       while(nItem == nNumberOfSlots) Full.P();
         
       /* SIMULATES INSERT AN ITEM IN THE SHARED LOCATION.*/

       nItem++;
       nNumberOfItemsProduced++;

       if(nItem == 1) Empty.V();
       Block.V();
     }/* End of Insert.*/

     /*
      * Gets the items from the shared memory location. This basically 
      * simulates picking of data.
     */
     public int Get(BoundedBufferCMon cThis){

      WBlock.P();
      /* Check if the total number of items produced by the producer
         are consumed.*/
      if(nNumberOfItemsConsumed >= nEvents){ 
       cThis.SetEndCondition(false);
       WBlock.V();
       return -1;
      }/* End if.*/
        
      while(nItem == 0) Empty.P();
       
      /* SIMULATES GET ITEM FROM THE SHARED LOCATION.*/

      nItem--;
      nNumberOfItemsConsumed++;

      if(nItem == nNumberOfSlots-1) Full.V();
      WBlock.V();
      return 1;
     }/* End of Get.*/

}/* End of BoundedBufferSem.*/

