< prev index next >

test/jdk/java/util/concurrent/locks/Lock/TimedAcquireLeak.java

Print this page
8229442: AQS and lock classes refresh
Reviewed-by: martin


  25  * @test
  26  * @bug 6460501 6236036 6500694 6490770
  27  * @summary Repeated failed timed waits shouldn't leak memory
  28  * @library /test/lib
  29  * @author Martin Buchholz
  30  */
  31 
  32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  33 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  34 
  35 import java.io.File;
  36 import java.io.FileOutputStream;
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import java.io.InputStreamReader;
  40 import java.io.OutputStream;
  41 import java.io.PrintStream;
  42 import java.io.Reader;
  43 import java.lang.ref.ReferenceQueue;
  44 import java.lang.ref.WeakReference;


  45 import java.util.concurrent.BlockingQueue;
  46 import java.util.concurrent.Callable;
  47 import java.util.concurrent.CountDownLatch;
  48 import java.util.concurrent.CyclicBarrier;
  49 import java.util.concurrent.ExecutorService;
  50 import java.util.concurrent.Executors;
  51 import java.util.concurrent.Future;
  52 import java.util.concurrent.LinkedBlockingQueue;
  53 import java.util.concurrent.Semaphore;
  54 import java.util.concurrent.ThreadLocalRandom;
  55 import java.util.concurrent.locks.ReentrantLock;
  56 import java.util.concurrent.locks.ReentrantReadWriteLock;
  57 import java.util.regex.Matcher;
  58 import java.util.regex.Pattern;
  59 import jdk.test.lib.Utils;
  60 
  61 public class TimedAcquireLeak {
  62     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
  63 
  64     static String javahome() {
  65         String jh = System.getProperty("java.home");
  66         return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
  67     }
  68 
  69     static final File bin = new File(javahome(), "bin");
  70 
  71     static String javaProgramPath(String programName) {
  72         return new File(bin, programName).getPath();
  73     }
  74 
  75     static final String java = javaProgramPath("java");
  76     static final String jmap = javaProgramPath("jmap");
  77     static final String jps  = javaProgramPath("jps");
  78 
  79     static String outputOf(Reader r) throws IOException {
  80         final StringBuilder sb = new StringBuilder();
  81         final char[] buf = new char[1024];
  82         int n;
  83         while ((n = r.read(buf)) > 0)
  84             sb.append(buf, 0, n);
  85         return sb.toString();
  86     }
  87 
  88     static String outputOf(InputStream is) throws IOException {
  89         return outputOf(new InputStreamReader(is, "UTF-8"));
  90     }
  91 
  92     static final ExecutorService drainers = Executors.newFixedThreadPool(12);
  93     static Future<String> futureOutputOf(final InputStream is) {
  94         return drainers.submit(
  95             new Callable<String>() { public String call() throws IOException {
  96                     return outputOf(is); }});}
  97 


 142                 }
 143                 timeoutMillis *= 4;
 144             }
 145         } catch (InterruptedException unexpected) {
 146             throw new AssertionError("unexpected InterruptedException");
 147         }
 148         throw new AssertionError("failed to do a \"full\" gc");
 149     }
 150 
 151     // To be called exactly twice by the child process
 152     public static void rendezvousChild() {
 153         try {
 154             forceFullGc();
 155             sendByte(System.out);
 156             System.in.read();
 157         } catch (Throwable t) { throw new Error(t); }
 158     }
 159 
 160     static String match(String s, String regex, int group) {
 161         Matcher matcher = Pattern.compile(regex).matcher(s);
 162         matcher.find();




 163         return matcher.group(group);
 164     }
 165 
 166     /** It's all about sending a message! */
 167     static void sendByte(OutputStream s) throws IOException {
 168         s.write('!');
 169         s.flush();
 170     }
 171 
 172     static int objectsInUse(final Process child,
 173                             final String childPid,
 174                             final String className) {
 175         final String regex =
 176             "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +\\Q"+className+"\\E(?:$| )";
 177         final Callable<Integer> objectsInUse =
 178             new Callable<Integer>() { public Integer call() {
 179                 Integer i = Integer.parseInt(
 180                     match(commandOutputOf(jmap, "-histo:live", childPid),
 181                           regex, 1));
 182                 if (i > 100)
 183                     System.out.print(
 184                         commandOutputOf(jmap,
 185                                         "-dump:file=dump,format=b",
 186                                         childPid));
 187                 return i;
 188             }};
 189         try { return rendezvousParent(child, objectsInUse); }
 190         catch (Throwable t) { unexpected(t); return -1; }
 191     }
 192 
 193     static void realMain(String[] args) throws Throwable {
 194         // jmap doesn't work on Windows
 195         if (System.getProperty("os.name").startsWith("Windows"))
 196             return;
 197 
 198         final String childClassName = Job.class.getName();
 199         final String classToCheckForLeaks = Job.classToCheckForLeaks();
 200         final String uniqueID =
 201             String.valueOf(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
 202 
 203         final String[] jobCmd = {
 204             java, "-Xmx8m", "-XX:+UsePerfData",
 205             "-classpath", System.getProperty("test.class.path"),
 206             childClassName, uniqueID
 207         };

 208         final Process p = new ProcessBuilder(jobCmd).start();
 209         // Ensure subprocess jvm has started, so that jps can find it
 210         p.getInputStream().read();
 211         sendByte(p.getOutputStream());
 212 
 213         final String childPid =
 214             match(commandOutputOf(jps, "-m"),
 215                   "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
 216 
 217         final int n0 = objectsInUse(p, childPid, classToCheckForLeaks);
 218         final int n1 = objectsInUse(p, childPid, classToCheckForLeaks);
 219         equal(p.waitFor(), 0);
 220         equal(p.exitValue(), 0);
 221         failed += p.exitValue();
 222 
 223         // Check that no objects were leaked.
 224         //
 225         // TODO: This test is very brittle, depending on current JDK
 226         // implementation, and needing occasional adjustment.
 227         System.out.printf("%d -> %d%n", n0, n1);
 228         // Almost always n0 == n1
 229         // Maximum jitter observed in practice is 10 -> 17
 230         check(Math.abs(n1 - n0) < 10);
 231         check(n1 < 25);
 232         drainers.shutdown();
 233         if (!drainers.awaitTermination(LONG_DELAY_MS, MILLISECONDS)) {
 234             drainers.shutdownNow(); // last resort
 235             throw new AssertionError("thread pool did not terminate");
 236         }
 237     }
 238 
 239     //----------------------------------------------------------------
 240     // The main class of the child process.
 241     // Job's job is to:
 242     // - provide the name of a class to check for leaks.
 243     // - call rendezvousChild exactly twice, while quiescent.
 244     // - in between calls to rendezvousChild, run code that may leak.
 245     //----------------------------------------------------------------
 246     public static class Job {
 247         static String classToCheckForLeaks() {
 248             return
 249                 "java.util.concurrent.locks.AbstractQueuedSynchronizer$Node";
 250         }
 251 
 252         public static void main(String[] args) throws Throwable {
 253             // Synchronize with parent process, so that jps can find us
 254             sendByte(System.out);
 255             System.in.read();
 256 
 257             final ReentrantLock lock = new ReentrantLock();
 258             lock.lock();
 259 
 260             final ReentrantReadWriteLock rwlock
 261                 = new ReentrantReadWriteLock();
 262             final ReentrantReadWriteLock.ReadLock readLock
 263                 = rwlock.readLock();
 264             final ReentrantReadWriteLock.WriteLock writeLock
 265                 = rwlock.writeLock();
 266             rwlock.writeLock().lock();
 267 
 268             final BlockingQueue<Object> q = new LinkedBlockingQueue<>();
 269             final Semaphore fairSem = new Semaphore(0, true);




  25  * @test
  26  * @bug 6460501 6236036 6500694 6490770
  27  * @summary Repeated failed timed waits shouldn't leak memory
  28  * @library /test/lib
  29  * @author Martin Buchholz
  30  */
  31 
  32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  33 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  34 
  35 import java.io.File;
  36 import java.io.FileOutputStream;
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import java.io.InputStreamReader;
  40 import java.io.OutputStream;
  41 import java.io.PrintStream;
  42 import java.io.Reader;
  43 import java.lang.ref.ReferenceQueue;
  44 import java.lang.ref.WeakReference;
  45 import java.util.ArrayList;
  46 import java.util.Collections;
  47 import java.util.concurrent.BlockingQueue;
  48 import java.util.concurrent.Callable;
  49 import java.util.concurrent.CountDownLatch;
  50 import java.util.concurrent.CyclicBarrier;
  51 import java.util.concurrent.ExecutorService;
  52 import java.util.concurrent.Executors;
  53 import java.util.concurrent.Future;
  54 import java.util.concurrent.LinkedBlockingQueue;
  55 import java.util.concurrent.Semaphore;
  56 import java.util.concurrent.ThreadLocalRandom;
  57 import java.util.concurrent.locks.ReentrantLock;
  58 import java.util.concurrent.locks.ReentrantReadWriteLock;
  59 import java.util.regex.Matcher;
  60 import java.util.regex.Pattern;
  61 import jdk.test.lib.Utils;
  62 
  63 public class TimedAcquireLeak {
  64     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
  65 
  66     static String javahome() {
  67         String jh = System.getProperty("java.home");
  68         return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
  69     }
  70 
  71     static final File bin = new File(javahome(), "bin");
  72 
  73     static String javaProgramPath(String programName) {
  74         return new File(bin, programName).getPath();
  75     }
  76 
  77     static final String javaPath = javaProgramPath("java");
  78     static final String jmapPath = javaProgramPath("jmap");
  79     static final String jpsPath  = javaProgramPath("jps");
  80 
  81     static String outputOf(Reader r) throws IOException {
  82         final StringBuilder sb = new StringBuilder();
  83         final char[] buf = new char[1024];
  84         int n;
  85         while ((n = r.read(buf)) > 0)
  86             sb.append(buf, 0, n);
  87         return sb.toString();
  88     }
  89 
  90     static String outputOf(InputStream is) throws IOException {
  91         return outputOf(new InputStreamReader(is, "UTF-8"));
  92     }
  93 
  94     static final ExecutorService drainers = Executors.newFixedThreadPool(12);
  95     static Future<String> futureOutputOf(final InputStream is) {
  96         return drainers.submit(
  97             new Callable<String>() { public String call() throws IOException {
  98                     return outputOf(is); }});}
  99 


 144                 }
 145                 timeoutMillis *= 4;
 146             }
 147         } catch (InterruptedException unexpected) {
 148             throw new AssertionError("unexpected InterruptedException");
 149         }
 150         throw new AssertionError("failed to do a \"full\" gc");
 151     }
 152 
 153     // To be called exactly twice by the child process
 154     public static void rendezvousChild() {
 155         try {
 156             forceFullGc();
 157             sendByte(System.out);
 158             System.in.read();
 159         } catch (Throwable t) { throw new Error(t); }
 160     }
 161 
 162     static String match(String s, String regex, int group) {
 163         Matcher matcher = Pattern.compile(regex).matcher(s);
 164         if (! matcher.find()) {
 165             String msg = String.format(
 166                 "match failed: s=%s regex=%s", s, regex);
 167             throw new AssertionError(msg);
 168         }
 169         return matcher.group(group);
 170     }
 171 
 172     /** It's all about sending a message! */
 173     static void sendByte(OutputStream s) throws IOException {
 174         s.write('!');
 175         s.flush();
 176     }
 177 
 178     static int objectsInUse(final Process child,
 179                             final String childPid,
 180                             final String classNameRegex) {
 181         String regex =
 182             "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +"+classNameRegex+"(?:$| )";
 183         Callable<Integer> objectsInUse = () -> {
 184             int i = Integer.parseInt(
 185                 match(commandOutputOf(jmapPath, "-histo:live", childPid),

 186                       regex, 1));
 187             if (i > 100)
 188                 System.out.print(
 189                     commandOutputOf(jmapPath,
 190                                     "-dump:file=dump,format=b",
 191                                     childPid));
 192             return i;
 193         };
 194         try { return rendezvousParent(child, objectsInUse); }
 195         catch (Throwable t) { unexpected(t); return -1; }
 196     }
 197 
 198     static void realMain(String[] args) throws Throwable {
 199         // jmap doesn't work on Windows
 200         if (System.getProperty("os.name").startsWith("Windows"))
 201             return;
 202 
 203         final String childClassName = Job.class.getName();
 204         final String classNameRegex = Job.classNameRegexToCheckForLeaks();
 205         final String uniqueID = String.valueOf(
 206             ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
 207 
 208         final ArrayList<String> jobCmd = new ArrayList<>();
 209         Collections.addAll(
 210             jobCmd, javaPath, "-Xmx8m", "-XX:+UsePerfData",
 211             "-classpath", System.getProperty("test.class.path"));
 212         Collections.addAll(jobCmd, Utils.getTestJavaOpts());
 213         Collections.addAll(jobCmd, childClassName, uniqueID);
 214         final Process p = new ProcessBuilder(jobCmd).start();
 215         // Ensure subprocess jvm has started, so that jps can find it
 216         p.getInputStream().read();
 217         sendByte(p.getOutputStream());
 218 
 219         final String childPid =
 220             match(commandOutputOf(jpsPath, "-m"),
 221                   "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
 222 
 223         final int n0 = objectsInUse(p, childPid, classNameRegex);
 224         final int n1 = objectsInUse(p, childPid, classNameRegex);
 225         equal(p.waitFor(), 0);
 226         equal(p.exitValue(), 0);
 227         failed += p.exitValue();
 228 
 229         // Check that no objects were leaked.
 230         //
 231         // TODO: This test is very brittle, depending on current JDK
 232         // implementation, and needing occasional adjustment.
 233         System.out.printf("%d -> %d%n", n0, n1);
 234         // Almost always n0 == n1
 235         // Maximum jitter observed in practice is 7
 236         check(Math.abs(n1 - n0) < 10);
 237         check(n1 < 25);
 238         drainers.shutdown();
 239         if (!drainers.awaitTermination(LONG_DELAY_MS, MILLISECONDS)) {
 240             drainers.shutdownNow(); // last resort
 241             throw new AssertionError("thread pool did not terminate");
 242         }
 243     }
 244 
 245     //----------------------------------------------------------------
 246     // The main class of the child process.
 247     // Job's job is to:
 248     // - provide the name of a class to check for leaks.
 249     // - call rendezvousChild exactly twice, while quiescent.
 250     // - in between calls to rendezvousChild, run code that may leak.
 251     //----------------------------------------------------------------
 252     public static class Job {
 253         static String classNameRegexToCheckForLeaks() {
 254             return
 255                 "\\Qjava.util.concurrent.locks.AbstractQueuedSynchronizer$\\E[A-Za-z]+";
 256         }
 257 
 258         public static void main(String[] args) throws Throwable {
 259             // Synchronize with parent process, so that jps can find us
 260             sendByte(System.out);
 261             System.in.read();
 262 
 263             final ReentrantLock lock = new ReentrantLock();
 264             lock.lock();
 265 
 266             final ReentrantReadWriteLock rwlock
 267                 = new ReentrantReadWriteLock();
 268             final ReentrantReadWriteLock.ReadLock readLock
 269                 = rwlock.readLock();
 270             final ReentrantReadWriteLock.WriteLock writeLock
 271                 = rwlock.writeLock();
 272             rwlock.writeLock().lock();
 273 
 274             final BlockingQueue<Object> q = new LinkedBlockingQueue<>();
 275             final Semaphore fairSem = new Semaphore(0, true);


< prev index next >