code: // from gee.cs.oswego.edu/home/jsr166/jsr166
  
  package jsr166tests.jtreg.util.concurrent.CountDownLatch;
  
  /*
   * Copyright 2005 Sun Microsystems, Inc.  All Rights Reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   * CA 95054 USA or visit www.sun.com if you need additional information or
   * have any questions.
   */
  
  /*
   * @test
   * @bug 6332435
   * @summary Basic tests for CountDownLatch
   * @author Seetharam Avadhanam, Martin Buchholz
   */
  
  import java.util.concurrent.*;
  import java.util.concurrent.atomic.AtomicInteger;
  
  interface AwaiterFactory {
      Awaiter getAwaiter();
  }
  
  abstract class Awaiter extends Thread {
      private volatile Throwable result = null;
      protected void result(Throwable result) { this.result = result; }
      public Throwable result() { return this.result; }
  }
  
  public class Basic {
  
      private void toTheStartingGate(CountDownLatch gate) {
          try {
              gate.await();
          }
          catch (Throwable t) { fail(t); }
      }
  
      private Awaiter awaiter(final CountDownLatch latch,
                              final CountDownLatch gate) {
          return new Awaiter() { public void run() {
              System.out.println("without millis: " + latch.toString());
              gate.countDown();
  
              try {
                  latch.await();
                  System.out.println("without millis - ComingOut");
              }
              catch (Throwable result) { result(result); }}};
      }
  
      private Awaiter awaiter(final CountDownLatch latch,
                              final CountDownLatch gate,
                              final long millis) {
          return new Awaiter() { public void run() {
              System.out.println("with millis: "+latch.toString());
              gate.countDown();
  
              try {
                  latch.await(millis, TimeUnit.MILLISECONDS);
                  System.out.println("with millis - ComingOut");
              }
              catch (Throwable result) { result(result); }}};
      }
  
      private AwaiterFactory awaiterFactories(final CountDownLatch latch,
                                              final CountDownLatch gate,
                                              final int i) {
          if (i == 1)
              return new AwaiterFactory() { public Awaiter getAwaiter() {
                  return awaiter(latch, gate); }};
  
          return new AwaiterFactory() { public Awaiter getAwaiter() {
              return awaiter(latch, gate, 10000); }};
      }
  
      //----------------------------------------------------------------
      // Normal use
      //----------------------------------------------------------------
      public static void normalUse() throws Throwable {
          int count = 0;
          Basic test = new Basic();
          CountDownLatch latch = new CountDownLatch(3);
          Awaiter a[] = new Awaiter[12];
  
          for (int i = 0; i < 3; i++) {
              CountDownLatch gate = new CountDownLatch(4);
              AwaiterFactory factory1 = test.awaiterFactories(latch, gate, 1);
              AwaiterFactory factory2 = test.awaiterFactories(latch, gate, 0);
              a[count] = factory1.getAwaiter(); a[count++].start();
              a[count] = factory1.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              test.toTheStartingGate(gate);
              System.out.println("Main Thread: " + latch.toString());
              latch.countDown();
              checkCount(latch, 2-i);
          }
          for (int i = 0; i < 12; i++)
              a[i].join();
  
          for (int i = 0; i < 12; i++)
              checkResult(a[i], null);
      }
  
      //----------------------------------------------------------------
      // One thread interrupted
      //----------------------------------------------------------------
      public static void threadInterrupted() throws Throwable{
          int count = 0;
          Basic test = new Basic();
          CountDownLatch latch = new CountDownLatch(3);
          Awaiter a[] = new Awaiter[12];
  
          for (int i = 0; i < 3; i++) {
              CountDownLatch gate = new CountDownLatch(4);
              AwaiterFactory factory1 = test.awaiterFactories(latch, gate, 1);
              AwaiterFactory factory2 = test.awaiterFactories(latch, gate, 0);
              a[count] = factory1.getAwaiter(); a[count++].start();
              a[count] = factory1.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              a[count-1].interrupt();
              test.toTheStartingGate(gate);
              System.out.println("Main Thread: " + latch.toString());
              latch.countDown();
              checkCount(latch, 2-i);
          }
          for (int i = 0; i < 12; i++)
              a[i].join();
  
          for (int i = 0; i < 12; i++)
              checkResult(a[i],
                          (i % 4) == 3 ? InterruptedException.class : null);
      }
  
      //----------------------------------------------------------------
      // One thread timed out
      //----------------------------------------------------------------
      public static void timeOut() throws Throwable {
          int count =0;
          Basic test = new Basic();
          CountDownLatch latch = new CountDownLatch(3);
          Awaiter a[] = new Awaiter[12];
  
          long[] timeout = { 0L, 5L, 10L };
  
          for (int i = 0; i < 3; i++) {
              CountDownLatch gate = new CountDownLatch(4);
              AwaiterFactory factory1 = test.awaiterFactories(latch, gate, 1);
              AwaiterFactory factory2 = test.awaiterFactories(latch, gate, 0);
              a[count] = test.awaiter(latch, gate, timeout[i]); a[count++].start();
              a[count] = factory1.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              a[count] = factory2.getAwaiter(); a[count++].start();
              test.toTheStartingGate(gate);
              System.out.println("Main Thread: " + latch.toString());
              latch.countDown();
              checkCount(latch, 2-i);
          }
          for (int i = 0; i < 12; i++)
              a[i].join();
  
          for (int i = 0; i < 12; i++)
              checkResult(a[i], null);
      }
  
      public static void main(String[] args) throws Throwable {
          normalUse();
          threadInterrupted();
          timeOut();
          if (failures.get() > 0L)
              throw new AssertionError(failures.get() + " failures");
      }
  
      private static final AtomicInteger failures = new AtomicInteger(0);
  
      private static void fail(String msg) {
          fail(new AssertionError(msg));
      }
  
      private static void fail(Throwable t) {
          t.printStackTrace();
          failures.getAndIncrement();
      }
  
      private static void checkCount(CountDownLatch b, int expected) {
          if (b.getCount() != expected)
              fail("Count = " + b.getCount() +
                   ", expected = " + expected);
      }
  
      private static void checkResult(Awaiter a, Class c) {
          Throwable t = a.result();
          if (! ((t == null && c == null) || c.isInstance(t))) {
              System.out.println("Mismatch: " + t + ", " + c.getName());
              failures.getAndIncrement();
          }
      }
  }
  