/*
Filename: ReadWriteController.java
Author: Professor Needham
Course: SI411

A class that can be used to govern Read/Write access to a shared
structure.

Class uses synchronized methods and the
anonymous object lock to oversee any reading and
writing threads.

Fair to both readers and writers in that
as soon as a writer arrives (while the database is being
read, say) then no more newly arriving readers are allowed
to start reading.  Instead the newly arriving readers
must wait until after that writer has written.  That
writer will then sweep in (allow to read the database) those
readers that had to wait.  However a finishing writer
will sweep into reading the database ALL waiting readers,
even those readers that arrived after a writer that
arrived after the current writer that is just finishing.
*/

import java.util.*;

class ReadWriteController {

   private int numReaders = 0;
   private int numWriters = 0;
   private int numWaitingReaders = 0;
   private int numWaitingWriters = 0;
   private boolean okToWrite = true;
   private long startWaitingReadersTime = 0;
   private final long startTime = System.currentTimeMillis();

   private long age(){  // time since program starts
     return System.currentTimeMillis() - startTime;
   }

   public synchronized void startRead() {
      long readerArrivalTime = 0;
      if (numWaitingWriters > 0 || numWriters > 0) {
         numWaitingReaders++;
         readerArrivalTime = age();
         while (readerArrivalTime >= startWaitingReadersTime)
            try {wait();} catch (InterruptedException e) {}
         numWaitingReaders--;
      }
      numReaders++;
   }

   public synchronized void endRead() {
      numReaders--;
      okToWrite = numReaders == 0;
      if (okToWrite) notifyAll();
   }

   public synchronized void startWrite() {
      if (numReaders > 0 || numWriters > 0) {
         numWaitingWriters++;
         okToWrite = false;
         while (!okToWrite)
            try {wait();} catch (InterruptedException e) {}
         numWaitingWriters--;
      }
      okToWrite = false;
      numWriters++;
   }

   public synchronized void endWrite() {
      numWriters--;              // ASSERT(numWriters==0)
      okToWrite = numWaitingReaders == 0;
      startWaitingReadersTime = age();
      notifyAll();
   }
}



