code: // from gee.cs.oswego.edu/home/jsr166/jsr166
  
  package jsr166tests.jtreg.util.concurrent.locks.Lock;
  
  /*
   * Copyright 2007 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 6460501 6236036 6500694 6490770
   * @summary Repeated failed timed waits shouldn't leak memory
   * @author Martin Buchholz
   */
  
  import java.util.*;
  import java.util.regex.*;
  import java.util.concurrent.*;
  import java.util.concurrent.locks.*;
  import static java.util.concurrent.TimeUnit.*;
  import java.io.*;
  
  public class TimedAcquireLeak {
      static String javahome() {
          String jh = System.getProperty("java.home");
          return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
      }
  
      static final File bin = new File(javahome(), "bin");
  
      static String javaProgramPath(String programName) {
          return new File(bin, programName).getPath();
      }
  
      static final String java = javaProgramPath("java");
      static final String jmap = javaProgramPath("jmap");
      static final String jps  = javaProgramPath("jps");
  
      static String outputOf(Reader r) throws IOException {
          final StringBuilder sb = new StringBuilder();
          final char[] buf = new char[1024];
          int n;
          while ((n = r.read(buf)) > 0)
              sb.append(buf, 0, n);
          return sb.toString();
      }
  
      static String outputOf(InputStream is) throws IOException {
          return outputOf(new InputStreamReader(is, "UTF-8"));
      }
  
      final static ExecutorService drainers = Executors.newFixedThreadPool(12);
      static Future<String> futureOutputOf(final InputStream is) {
          return drainers.submit(
              new Callable<String>() { public String call() throws IOException {
                      return outputOf(is); }});}
  
      static String outputOf(final Process p) {
          try {
              Future<String> outputFuture = futureOutputOf(p.getInputStream());
              Future<String> errorFuture = futureOutputOf(p.getErrorStream());
              final String output = outputFuture.get();
              final String error = errorFuture.get();
              // Check for successful process completion
              equal(error, "");
              equal(p.waitFor(), 0);
              equal(p.exitValue(), 0);
              return output;
          } catch (Throwable t) { unexpected(t); throw new Error(t); }
      }
  
      static String commandOutputOf(String... cmd) {
          try { return outputOf(new ProcessBuilder(cmd).start()); }
          catch (Throwable t) { unexpected(t); throw new Error(t); }
      }
  
      // To be called exactly twice by the parent process
      static <T> T rendezvousParent(Process p,
                                    Callable<T> callable) throws Throwable {
          p.getInputStream().read();
          T result = callable.call();
          OutputStream os = p.getOutputStream();
          os.write((byte)'\n'); os.flush();
          return result;
      }
  
      // To be called exactly twice by the child process
      public static void rendezvousChild() {
          try {
              for (int i = 0; i < 100; i++) {
                  System.gc(); System.runFinalization(); Thread.sleep(50);
              }
              System.out.write((byte)'\n'); System.out.flush();
              System.in.read();
          } catch (Throwable t) { throw new Error(t); }
      }
  
      static String match(String s, String regex, int group) {
          Matcher matcher = Pattern.compile(regex).matcher(s);
          matcher.find();
          return matcher.group(group);
      }
  
      static int objectsInUse(final Process child,
                              final String childPid,
                              final String className) {
          final String regex =
              "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +\\Q"+className+"\\E$";
          final Callable<Integer> objectsInUse =
              new Callable<Integer>() { public Integer call() {
                  Integer i = Integer.parseInt(
                      match(commandOutputOf(jmap, "-histo:live", childPid),
                            regex, 1));
                  if (i > 100)
                      System.out.print(
                          commandOutputOf(jmap,
                                          "-dump:file=dump,format=b",
                                          childPid));
                  return i;
              }};
          try { return rendezvousParent(child, objectsInUse); }
          catch (Throwable t) { unexpected(t); return -1; }
      }
  
      static void realMain(String[] args) throws Throwable {
          // jmap doesn't work on Windows
          if (System.getProperty("os.name").startsWith("Windows"))
              return;
  
          final String childClassName = Job.class.getName();
          final String classToCheckForLeaks = Job.classToCheckForLeaks();
          final String uniqueID =
              String.valueOf(new Random().nextInt(Integer.MAX_VALUE));
  
          final String[] jobCmd = {
              java, "-Xmx8m",
              "-classpath", System.getProperty("test.classes", "."),
              childClassName, uniqueID
          };
          final Process p = new ProcessBuilder(jobCmd).start();
  
          final String childPid =
              match(commandOutputOf(jps, "-m"),
                    "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
  
          final int n0 = objectsInUse(p, childPid, classToCheckForLeaks);
          final int n1 = objectsInUse(p, childPid, classToCheckForLeaks);
          equal(p.waitFor(), 0);
          equal(p.exitValue(), 0);
          failed += p.exitValue();
  
          // Check that no objects were leaked.
          System.out.printf("%d -> %d%n", n0, n1);
          check(Math.abs(n1 - n0) < 2); // Almost always n0 == n1
          check(n1 < 20);
          drainers.shutdown();
      }
  
      //----------------------------------------------------------------
      // The main class of the child process.
      // Job's job is to:
      // - provide the name of a class to check for leaks.
      // - call rendezvousChild exactly twice, while quiescent.
      // - in between calls to rendezvousChild, run code that may leak.
      //----------------------------------------------------------------
      public static class Job {
          static String classToCheckForLeaks() {
              return
                  "java.util.concurrent.locks.AbstractQueuedSynchronizer$Node";
          }
  
          public static void main(String[] args) throws Throwable {
              final ReentrantLock lock = new ReentrantLock();
              lock.lock();
  
              final ReentrantReadWriteLock rwlock
                  = new ReentrantReadWriteLock();
              final ReentrantReadWriteLock.ReadLock readLock
                  = rwlock.readLock();
              final ReentrantReadWriteLock.WriteLock writeLock
                  = rwlock.writeLock();
              rwlock.writeLock().lock();
  
              final BlockingQueue<Object> q = new LinkedBlockingQueue<Object>();
              final Semaphore fairSem = new Semaphore(0, true);
              final Semaphore unfairSem = new Semaphore(0, false);
              //final int threads =
              //rnd.nextInt(Runtime.getRuntime().availableProcessors() + 1) + 1;
              final int threads = 3;
              // On Linux, this test runs very slowly for some reason,
              // so use a smaller number of iterations.
              // Solaris can handle 1 << 18.
              // On the other hand, jmap is much slower on Solaris...
              final int iterations = 1 << 8;
              final CyclicBarrier cb = new CyclicBarrier(threads+1);
  
              for (int i = 0; i < threads; i++)
                  new Thread() { public void run() {
                      try {
                          final Random rnd = new Random();
                          for (int j = 0; j < iterations; j++) {
                              if (j == iterations/10 || j == iterations - 1) {
                                  cb.await(); // Quiesce
                                  cb.await(); // Resume
                              }
                              //int t = rnd.nextInt(2000);
                              int t = rnd.nextInt(900);
                              check(! lock.tryLock(t, NANOSECONDS));
                              check(! readLock.tryLock(t, NANOSECONDS));
                              check(! writeLock.tryLock(t, NANOSECONDS));
                              equal(null, q.poll(t, NANOSECONDS));
                              check(! fairSem.tryAcquire(t, NANOSECONDS));
                              check(! unfairSem.tryAcquire(t, NANOSECONDS));
                          }
                      } catch (Throwable t) { unexpected(t); }
                  }}.start();
  
              cb.await();         // Quiesce
              rendezvousChild();  // Measure
              cb.await();         // Resume
  
              cb.await();         // Quiesce
              rendezvousChild();  // Measure
              cb.await();         // Resume
  
              System.exit(failed);
          }
  
          // If something goes wrong, we might never see it, since IO
          // streams are connected to the parent.  So we need a special
          // purpose print method to debug Jobs.
          static void debugPrintf(String format, Object... args) {
              try {
                  new PrintStream(new FileOutputStream("/dev/tty"))
                      .printf(format, args);
              } catch (Throwable t) { throw new Error(t); }
          }
      }
  
      //--------------------- Infrastructure ---------------------------
      static volatile int passed = 0, failed = 0;
      static void pass() {passed++;}
      static void fail() {failed++; Thread.dumpStack();}
      static void fail(String msg) {System.out.println(msg); fail();}
      static void unexpected(Throwable t) {failed++; t.printStackTrace();}
      static void check(boolean cond) {if (cond) pass(); else fail();}
      static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
      static void equal(Object x, Object y) {
          if (x == null ? y == null : x.equals(y)) pass();
          else fail(x + " not equal to " + y);}
      public static void main(String[] args) throws Throwable {
          try {realMain(args);} catch (Throwable t) {unexpected(t);}
          System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
          if (failed > 0) throw new AssertionError("Some tests failed");}
  }
  