code: /*
   * Copyright (c) 2009 Stanford University, unless otherwise specified.
   * All rights reserved.
   *
   * This software was developed by the Pervasive Parallelism Laboratory of
   * Stanford University, California, USA.
   *
   * Permission to use, copy, modify, and distribute this software in source
   * or binary form for any purpose with or without fee is hereby granted,
   * provided that the following conditions are met:
   *
   *    1. Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *
   *    2. Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *
   *    3. Neither the name of Stanford University nor the names of its
   *       contributors may be used to endorse or promote products derived
   *       from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  package edu.stanford.ppl.concurrent;
  
  import junit.framework.TestCase;
  
  public class EpochTest extends TestCase {
      public void testImmediateClose() {
          final boolean[] closed = { false };
          final Epoch e = new Epoch() {
              protected void onClosed(final int dataSum) {
                  closed[0] = true;
                  assertEquals(0, dataSum);
              }
          };
          e.beginClose();
          assertTrue(closed[0]);
          assertNull(e.attemptArrive());
      }
  
      public void testSimple() {
          final boolean[] closed = { false };
          final Epoch e = new Epoch() {
              protected void onClosed(final int dataSum) {
                  closed[0] = true;
                  assertEquals(1, dataSum);
              }
          };
          final Epoch.Ticket t0 = e.attemptArrive();
          assertNotNull(t0);
          t0.leave(1);
          assertTrue(!closed[0]);
          e.beginClose();
          assertTrue(closed[0]);
          assertNull(e.attemptArrive());
      }
  
      public void testPending() {
          final boolean[] closed = { false };
          final Epoch e = new Epoch() {
              protected void onClosed(final int dataSum) {
                  closed[0] = true;
                  assertEquals(1, dataSum);
              }
          };
          final Epoch.Ticket t0 = e.attemptArrive();
          assertNotNull(t0);
          e.beginClose();
          assertTrue(!closed[0]);
          t0.leave(1);
          assertTrue(closed[0]);
          assertNull(e.attemptArrive());
      }
  
      public void testParallelCutoff() {
          final int numThreads = 32;
          final int arrivalsPerThread = 1000000;
          final boolean[] closed = { false };
          final Epoch e = new Epoch() {
              protected void onClosed(final int dataSum) {
                  closed[0] = true;
              }
          };
          ParUtil.parallel(numThreads, new ParUtil.Block() {
              public void call(final int index) {
                  for (int i = 0; i < arrivalsPerThread; ++i) {
                      final Epoch.Ticket t = e.attemptArrive();
                      if (t == null) {
                          //System.out.print("thread " + index + " got to " + i + "\n");
                          return;
                      }
                      t.leave(1 + index);
                      if (index == numThreads - 1 && i == arrivalsPerThread / 2) {
                          e.beginClose();
                      }
                  }
              }
          });
          assertTrue(closed[0]);
      }
  
      public void testParallelPerformance() {
          final int arrivalsPerThread = 1000000;
          for (int i = 0; i < 3; ++i) {
              for (int t = 1; t <= Runtime.getRuntime().availableProcessors(); t *= 2) {
                  runNoClosePerf(t, arrivalsPerThread);
              }
              for (int f = 2; f <= 4; f *= 2) {
                  runNoClosePerf(Runtime.getRuntime().availableProcessors() * f, arrivalsPerThread / f);
              }
          }
      }
  
      private void runNoClosePerf(final int numThreads, final int arrivalsPerThread) {
          final boolean[] closed = { false };
          final Epoch e = new Epoch() {
              protected void onClosed(final int dataSum) {
                  closed[0] = true;
                  assertEquals(numThreads * (numThreads + 1L) * arrivalsPerThread / 2, dataSum);
              }
          };
          final long elapsed = ParUtil.timeParallel(numThreads, new ParUtil.Block() {
              public void call(final int index) {
                  for (int i = 0; i < arrivalsPerThread; ++i) {
                      final Epoch.Ticket t = e.attemptArrive();
                      assertNotNull(t);
                      t.leave(1 + index);
                  }
              }
          });
          assertTrue(!closed[0]);
          e.beginClose();
          assertTrue(closed[0]);
          assertNull(e.attemptArrive());
  
          final long arrivalsPerSec = numThreads * 1000L * arrivalsPerThread / elapsed;
          System.out.println("numThreads " + numThreads + "    arrivalsPerThread " + arrivalsPerThread + "    " +
                  "elapsedMillis " + elapsed + "    arrivalsPerSec " + arrivalsPerSec + "    " +
                  "spread " + e.computeSpread());
      }
  }
  