package Synchronization;

import Utilities.*;

// a Barrier for m*n threads: all must arrive before any are released
public class Barrier extends MyObject implements Runnable {

   private int m = -1, n = -1;
   private CountingSemaphore arrive = null;
   private BinarySemaphore[][] release = null;
   private Thread thread = null;

   public Barrier(int m, int n) {
      super("Barrier for m=" + m + " n=" + n + " threads");
      this.m = m;
      this.n = n;
      arrive = new CountingSemaphore(0);
      release = new BinarySemaphore[m][n];
      for (int i = 0; i < m; i++) for (int j = 0; j < n; j++)
         release[i][j] = new BinarySemaphore(0);
      thread = new Thread(this);
      thread.setDaemon(true);
      thread.start();
   }

   public Barrier(int n) { this(1, n); }

   public String toString() { return getName() + ", arrive=" + arrive; }

   public void gate(int i, int j) {
      V(arrive);
      P(release[i][j]);
   }

   public void gate(int j) { this.gate(0, j); }

   // not really needed since this is a daemon thread
   public void stop() { thread.stop(); }

   public void run() {
      while (true) {
// If we are worried about two different threads mistakenly
// calling barrier(i) for the same i, we can make arrive into
// an array of semaphores like semaphore release already is.
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++)
            P(arrive);
// Second semaphore must be an array to avoid race conditions
// due to arbitrary context switches and relative CPU speeds.
         for (int i = 0; i < m; i++) for (int j = 0; j < n; j++)
            V(release[i][j]);
      }
   }
}
