Print this page
Split |
Close |
Expand all |
Collapse all |
--- old/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java
+++ new/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java
1 1 /*
2 2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation.
8 8 *
9 9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 12 * version 2 for more details (a copy is included in the LICENSE file that
13 13 * accompanied this code).
14 14 *
15 15 * You should have received a copy of the GNU General Public License version
16 16 * 2 along with this work; if not, write to the Free Software Foundation,
17 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 18 *
19 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 20 * or visit www.oracle.com if you need additional information or have any
21 21 * questions.
22 22 */
23 23
24 24 /*
25 25 * @test
26 26 * @bug 6460501 6236036 6500694 6490770
27 27 * @summary Repeated failed timed waits shouldn't leak memory
28 28 * @author Martin Buchholz
29 29 */
30 30
31 31 import java.util.*;
32 32 import java.util.regex.*;
33 33 import java.util.concurrent.*;
34 34 import java.util.concurrent.locks.*;
35 35 import static java.util.concurrent.TimeUnit.*;
36 36 import java.io.*;
37 37
38 38 public class TimedAcquireLeak {
39 39 static String javahome() {
40 40 String jh = System.getProperty("java.home");
41 41 return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
42 42 }
43 43
44 44 static final File bin = new File(javahome(), "bin");
45 45
46 46 static String javaProgramPath(String programName) {
47 47 return new File(bin, programName).getPath();
48 48 }
49 49
50 50 static final String java = javaProgramPath("java");
51 51 static final String jmap = javaProgramPath("jmap");
52 52 static final String jps = javaProgramPath("jps");
53 53
54 54 static String outputOf(Reader r) throws IOException {
55 55 final StringBuilder sb = new StringBuilder();
56 56 final char[] buf = new char[1024];
57 57 int n;
58 58 while ((n = r.read(buf)) > 0)
59 59 sb.append(buf, 0, n);
60 60 return sb.toString();
61 61 }
62 62
63 63 static String outputOf(InputStream is) throws IOException {
64 64 return outputOf(new InputStreamReader(is, "UTF-8"));
65 65 }
66 66
67 67 static final ExecutorService drainers = Executors.newFixedThreadPool(12);
68 68 static Future<String> futureOutputOf(final InputStream is) {
69 69 return drainers.submit(
70 70 new Callable<String>() { public String call() throws IOException {
71 71 return outputOf(is); }});}
72 72
73 73 static String outputOf(final Process p) {
74 74 try {
75 75 Future<String> outputFuture = futureOutputOf(p.getInputStream());
76 76 Future<String> errorFuture = futureOutputOf(p.getErrorStream());
77 77 final String output = outputFuture.get();
78 78 final String error = errorFuture.get();
79 79 // Check for successful process completion
80 80 equal(error, "");
81 81 equal(p.waitFor(), 0);
82 82 equal(p.exitValue(), 0);
83 83 return output;
84 84 } catch (Throwable t) { unexpected(t); throw new Error(t); }
85 85 }
86 86
87 87 static String commandOutputOf(String... cmd) {
88 88 try { return outputOf(new ProcessBuilder(cmd).start()); }
89 89 catch (Throwable t) { unexpected(t); throw new Error(t); }
90 90 }
91 91
92 92 // To be called exactly twice by the parent process
93 93 static <T> T rendezvousParent(Process p,
94 94 Callable<T> callable) throws Throwable {
95 95 p.getInputStream().read();
96 96 T result = callable.call();
97 97 OutputStream os = p.getOutputStream();
98 98 os.write((byte)'\n'); os.flush();
99 99 return result;
100 100 }
101 101
102 102 // To be called exactly twice by the child process
103 103 public static void rendezvousChild() {
104 104 try {
105 105 for (int i = 0; i < 100; i++) {
106 106 System.gc(); System.runFinalization(); Thread.sleep(50);
107 107 }
108 108 System.out.write((byte)'\n'); System.out.flush();
109 109 System.in.read();
110 110 } catch (Throwable t) { throw new Error(t); }
111 111 }
112 112
113 113 static String match(String s, String regex, int group) {
114 114 Matcher matcher = Pattern.compile(regex).matcher(s);
115 115 matcher.find();
116 116 return matcher.group(group);
117 117 }
118 118
119 119 static int objectsInUse(final Process child,
120 120 final String childPid,
121 121 final String className) {
122 122 final String regex =
123 123 "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +\\Q"+className+"\\E$";
124 124 final Callable<Integer> objectsInUse =
125 125 new Callable<Integer>() { public Integer call() {
126 126 Integer i = Integer.parseInt(
127 127 match(commandOutputOf(jmap, "-histo:live", childPid),
128 128 regex, 1));
129 129 if (i > 100)
130 130 System.out.print(
131 131 commandOutputOf(jmap,
132 132 "-dump:file=dump,format=b",
133 133 childPid));
134 134 return i;
135 135 }};
136 136 try { return rendezvousParent(child, objectsInUse); }
137 137 catch (Throwable t) { unexpected(t); return -1; }
138 138 }
139 139
140 140 static void realMain(String[] args) throws Throwable {
↓ open down ↓ |
140 lines elided |
↑ open up ↑ |
141 141 // jmap doesn't work on Windows
142 142 if (System.getProperty("os.name").startsWith("Windows"))
143 143 return;
144 144
145 145 final String childClassName = Job.class.getName();
146 146 final String classToCheckForLeaks = Job.classToCheckForLeaks();
147 147 final String uniqueID =
148 148 String.valueOf(new Random().nextInt(Integer.MAX_VALUE));
149 149
150 150 final String[] jobCmd = {
151 - java, "-Xmx8m",
151 + java, "-Xmx8m", "-XX:+UsePerfData",
152 152 "-classpath", System.getProperty("test.classes", "."),
153 153 childClassName, uniqueID
154 154 };
155 155 final Process p = new ProcessBuilder(jobCmd).start();
156 156
157 157 final String childPid =
158 158 match(commandOutputOf(jps, "-m"),
159 159 "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
160 160
161 161 final int n0 = objectsInUse(p, childPid, classToCheckForLeaks);
162 162 final int n1 = objectsInUse(p, childPid, classToCheckForLeaks);
163 163 equal(p.waitFor(), 0);
164 164 equal(p.exitValue(), 0);
165 165 failed += p.exitValue();
166 166
167 167 // Check that no objects were leaked.
168 168 System.out.printf("%d -> %d%n", n0, n1);
169 169 check(Math.abs(n1 - n0) < 2); // Almost always n0 == n1
170 170 check(n1 < 20);
171 171 drainers.shutdown();
172 172 }
173 173
174 174 //----------------------------------------------------------------
175 175 // The main class of the child process.
176 176 // Job's job is to:
177 177 // - provide the name of a class to check for leaks.
178 178 // - call rendezvousChild exactly twice, while quiescent.
179 179 // - in between calls to rendezvousChild, run code that may leak.
180 180 //----------------------------------------------------------------
181 181 public static class Job {
182 182 static String classToCheckForLeaks() {
183 183 return
184 184 "java.util.concurrent.locks.AbstractQueuedSynchronizer$Node";
185 185 }
186 186
187 187 public static void main(String[] args) throws Throwable {
188 188 final ReentrantLock lock = new ReentrantLock();
189 189 lock.lock();
190 190
191 191 final ReentrantReadWriteLock rwlock
192 192 = new ReentrantReadWriteLock();
193 193 final ReentrantReadWriteLock.ReadLock readLock
194 194 = rwlock.readLock();
195 195 final ReentrantReadWriteLock.WriteLock writeLock
196 196 = rwlock.writeLock();
197 197 rwlock.writeLock().lock();
198 198
199 199 final BlockingQueue<Object> q = new LinkedBlockingQueue<Object>();
200 200 final Semaphore fairSem = new Semaphore(0, true);
201 201 final Semaphore unfairSem = new Semaphore(0, false);
202 202 //final int threads =
203 203 //rnd.nextInt(Runtime.getRuntime().availableProcessors() + 1) + 1;
204 204 final int threads = 3;
205 205 // On Linux, this test runs very slowly for some reason,
206 206 // so use a smaller number of iterations.
207 207 // Solaris can handle 1 << 18.
208 208 // On the other hand, jmap is much slower on Solaris...
209 209 final int iterations = 1 << 8;
210 210 final CyclicBarrier cb = new CyclicBarrier(threads+1);
211 211
212 212 for (int i = 0; i < threads; i++)
213 213 new Thread() { public void run() {
214 214 try {
215 215 final Random rnd = new Random();
216 216 for (int j = 0; j < iterations; j++) {
217 217 if (j == iterations/10 || j == iterations - 1) {
218 218 cb.await(); // Quiesce
219 219 cb.await(); // Resume
220 220 }
221 221 //int t = rnd.nextInt(2000);
222 222 int t = rnd.nextInt(900);
223 223 check(! lock.tryLock(t, NANOSECONDS));
224 224 check(! readLock.tryLock(t, NANOSECONDS));
225 225 check(! writeLock.tryLock(t, NANOSECONDS));
226 226 equal(null, q.poll(t, NANOSECONDS));
227 227 check(! fairSem.tryAcquire(t, NANOSECONDS));
228 228 check(! unfairSem.tryAcquire(t, NANOSECONDS));
229 229 }
230 230 } catch (Throwable t) { unexpected(t); }
231 231 }}.start();
232 232
233 233 cb.await(); // Quiesce
234 234 rendezvousChild(); // Measure
235 235 cb.await(); // Resume
236 236
237 237 cb.await(); // Quiesce
238 238 rendezvousChild(); // Measure
239 239 cb.await(); // Resume
240 240
241 241 System.exit(failed);
242 242 }
243 243
244 244 // If something goes wrong, we might never see it, since IO
245 245 // streams are connected to the parent. So we need a special
246 246 // purpose print method to debug Jobs.
247 247 static void debugPrintf(String format, Object... args) {
248 248 try {
249 249 new PrintStream(new FileOutputStream("/dev/tty"))
250 250 .printf(format, args);
251 251 } catch (Throwable t) { throw new Error(t); }
252 252 }
253 253 }
254 254
255 255 //--------------------- Infrastructure ---------------------------
256 256 static volatile int passed = 0, failed = 0;
257 257 static void pass() {passed++;}
258 258 static void fail() {failed++; Thread.dumpStack();}
259 259 static void fail(String msg) {System.out.println(msg); fail();}
260 260 static void unexpected(Throwable t) {failed++; t.printStackTrace();}
261 261 static void check(boolean cond) {if (cond) pass(); else fail();}
262 262 static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
263 263 static void equal(Object x, Object y) {
264 264 if (x == null ? y == null : x.equals(y)) pass();
265 265 else fail(x + " not equal to " + y);}
266 266 public static void main(String[] args) throws Throwable {
267 267 try {realMain(args);} catch (Throwable t) {unexpected(t);}
268 268 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
269 269 if (failed > 0) throw new AssertionError("Some tests failed");}
270 270 }
↓ open down ↓ |
109 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX