package Synchronization;

public abstract class Semaphore {

   // if value < 0, then abs(value) is the size of the P() queue
   protected int value = 0;

   protected Semaphore() {value = 0;}

   protected Semaphore(int initial) {value = initial;}

   public synchronized void P() {
      value--;
      if (value < 0) {
         while (true) {     // we must be notified not interrupted
            try {
               wait();
               break;       // notify(), so P() succeeds
            } catch (InterruptedException e) {
               System.err.println
                  ("Semaphore.P(): InterruptedException, wait again");
               continue;    // no V() yet
            }
         }
      }
   }

   public synchronized void V() { // this technique prevents
      value++;                    // barging since any caller of
      if (value <= 0) notify();   // P() will wait even if it
   }                              // enters before signaled thread

   // do not do a `if (S.value() > 0) S.P(); else ...'
   // because there is a race condition; use S.tryP() instead
   public synchronized int value() {
      return value;
   }

   public synchronized String toString() {
      return String.valueOf(value);
   }

   public synchronized void tryP() throws WouldBlockException {
      if (value > 0) this.P();
      else throw new WouldBlockException();
   }
}
