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 java.util.concurrent.CyclicBarrier;
  
  
  public class ParUtil {
      public interface Block {
          void call(int index);
      }
  
      private static class RunnableBlock implements Block {
          private final Runnable _task;
  
          RunnableBlock(final Runnable task) {
              _task = task;
          }
  
          public void call(final int index) {
              _task.run();
          }
      }
  
      public static void parallel(final int numThreads, final Runnable block) {
          parallel(numThreads, new RunnableBlock(block));
      }
  
      public static void parallel(final int numThreads, final Block block) {
          final Thread[] threads = new Thread[numThreads];
          final Throwable[] failure = { null };
          for (int i = 0; i < threads.length; ++i) {
              final int index = i;
              threads[i] = new Thread("worker #" + i) {
                  @Override
                  public void run() {
                      try {
                          block.call(index);
                      }
                      catch (final Throwable xx) {
                          failure[0] = xx;
                      }
                  }
              };
          }
          for (Thread t : threads) {
              t.start();
          }
          for (Thread t : threads) {
              try {
                  t.join();
              }
              catch (final InterruptedException xx) {
                  throw new RuntimeException("unexpected", xx);
              }
          }
  
          if (failure[0] instanceof RuntimeException) {
              throw (RuntimeException) failure[0];
          }
          else if (failure[0] instanceof Error) {
              throw (Error) failure[0];
          }
          else {
              assert(failure[0] == null);
          }
      }
  
      /** Returns the elapsed milliseconds. */
      public static long timeParallel(final int numThreads, final Runnable block) {
          return timeParallel(numThreads, new RunnableBlock(block));
      }
  
      /** Returns the elapsed milliseconds. */
      public static long timeParallel(final int numThreads, final Block block) {
          final long[] times = new long[2];
          final CyclicBarrier barrier = new CyclicBarrier(numThreads, new Runnable() {
              public void run() {
                  times[0] = times[1];
                  times[1] = System.currentTimeMillis();
              }
          });
          parallel(numThreads, new Block() {
              public void call(final int index) {
                  try {
                      barrier.await();
                  }
                  catch (final Exception xx) {
                      throw new RuntimeException("unexpected", xx);
                  }
                  try {
                      block.call(index);
                  }
                  finally {
                      try {
                          barrier.await();
                      }
                      catch (final Exception xx) {
                          throw new RuntimeException("unexpected", xx);
                      }
                  }
              }
          });
          return times[1] - times[0];
      }
  
  }
  