blob: d8a9b7d5da0a7d7104c92ac4dc65d45156e5600c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 4530538
27 * @summary Basic unit test of the synchronization statistics support:
28 *
29 * @author Mandy Chung
30 *
31 * @ignore 6309226
32 * @build Semaphore
33 * @run main SynchronizationStatistics
34 */
35
36import java.lang.management.*;
37
38public class SynchronizationStatistics {
39 private static ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
40
41 private static boolean blockedTimeCheck =
42 mbean.isThreadContentionMonitoringSupported();
43 private static boolean trace = false;
44
45 private static Object lockA = new Object();
46 private static Object lockB = new Object();
47 private static Object lockC = new Object();
48 private static Object lockD = new Object();
49 private static Object waiter = new Object();
50 private static volatile boolean testFailed = false;
51
52 private static Object go = new Object();
53
54 private static void goSleep(long ms) {
55 try {
56 Thread.sleep(ms);
57 } catch (InterruptedException e) {
58 e.printStackTrace();
59 System.out.println("Unexpected exception.");
60 testFailed = true;
61 }
62 }
63
64 public static void main(String args[]) throws Exception {
65 if (args.length > 0 && args[0].equals("trace")) {
66 trace = true;
67 }
68
69 if (blockedTimeCheck) {
70 mbean.setThreadContentionMonitoringEnabled(true);
71 }
72
73 if (!mbean.isThreadContentionMonitoringEnabled()) {
74 throw new RuntimeException("TEST FAILED: " +
75 "Thread Contention Monitoring is not enabled");
76 }
77
78 Examiner examiner = new Examiner("Examiner");
79 BlockedThread blocked = new BlockedThread("BlockedThread");
80 examiner.setThread(blocked);
81
82 // Start the threads and check them in Blocked and Waiting states
83 examiner.start();
84
85 // wait until the examiner acquires all the locks and waiting
86 // for the BlockedThread to start
87 examiner.waitUntilWaiting();
88
89 System.out.println("Checking the thread state for the examiner thread " +
90 "is waiting to begin.");
91
92 // The Examiner should be waiting to be notified by the BlockedThread
93 checkThreadState(examiner, Thread.State.WAITING);
94
95 System.out.println("Now starting the blocked thread");
96 blocked.start();
97
98 try {
99 examiner.join();
100 blocked.join();
101 } catch (InterruptedException e) {
102 e.printStackTrace();
103 System.out.println("Unexpected exception.");
104 testFailed = true;
105 }
106
107 if (testFailed)
108 throw new RuntimeException("TEST FAILED.");
109
110 System.out.println("Test passed.");
111 }
112
113 private static String INDENT = " ";
114 private static void printStack(Thread t, StackTraceElement[] stack) {
115 System.out.println(INDENT + t +
116 " stack: (length = " + stack.length + ")");
117 if (t != null) {
118 for (int j = 0; j < stack.length; j++) {
119 System.out.println(INDENT + stack[j]);
120 }
121 System.out.println();
122 }
123 }
124
125 private static void checkThreadState(Thread thread, Thread.State s)
126 throws Exception {
127
128 ThreadInfo ti = mbean.getThreadInfo(thread.getId());
129 if (ti.getThreadState() != s) {
130 ThreadInfo info = mbean.getThreadInfo(thread.getId(),
131 Integer.MAX_VALUE);
132 System.out.println(INDENT + "TEST FAILED:");
133 printStack(thread, info.getStackTrace());
134 System.out.println(INDENT + "Thread state: " + info.getThreadState());
135
136 throw new RuntimeException("TEST FAILED: " +
137 "Thread state for " + thread + " returns " + ti.getThreadState() +
138 ". Expected to be " + s);
139 }
140 }
141
142 private static void checkThreadState(Thread thread,
143 Thread.State s1, Thread.State s2)
144 throws Exception {
145
146 ThreadInfo ti = mbean.getThreadInfo(thread.getId());
147 if (ti.getThreadState() != s1 && ti.getThreadState() != s2) {
148 throw new RuntimeException("TEST FAILED: " +
149 "Thread state for " + thread + " returns " + ti.getThreadState() +
150 ". Expected to be " + s1 + " or " + s2);
151 }
152 }
153
154 static class StatThread extends Thread {
155 private long blockingBaseTime = 0;
156 private long totalWaitTime = 0;
157 private long totalBlockedEnterTime = 0;
158
159 StatThread(String name) {
160 super(name);
161 }
162
163 void addWaitTime(long ns) {
164 totalWaitTime = totalWaitTime + ns;
165 }
166 void addBlockedEnterTime(long ns) {
167 totalBlockedEnterTime = totalBlockedEnterTime + ns;
168 }
169 void setBlockingBaseTime(long time) {
170 blockingBaseTime = time;
171 }
172
173 long totalBlockedTimeMs() {
174 return totalBlockedEnterTime / 1000000;
175 }
176
177 long totalBlockedTimeMs(long now) {
178 long t = totalBlockedEnterTime + (now - blockingBaseTime);
179 return t / 1000000;
180 }
181
182 long totalWaitTimeMs() {
183 return totalWaitTime / 1000000;
184 }
185
186 long totalWaitTimeMs(long now) {
187 long t = totalWaitTime + (now - blockingBaseTime);
188 return t / 1000000;
189 }
190 }
191
192 static class BlockedThread extends StatThread {
193 private Semaphore handshake = new Semaphore();
194 BlockedThread(String name) {
195 super(name);
196 }
197 void waitUntilBlocked() {
198 handshake.semaP();
199
200 // give a chance for the examiner thread to really wait
201 goSleep(20);
202 }
203
204 void waitUntilWaiting() {
205 waitUntilBlocked();
206 }
207
208 boolean hasWaitersForBlocked() {
209 return (handshake.getWaiterCount() > 0);
210 }
211
212 private void notifyWaiter() {
213 // wait until the examiner waits on the semaphore
214 while (handshake.getWaiterCount() == 0) {
215 goSleep(20);
216 }
217 handshake.semaV();
218 }
219
220 private void waitObj(long ms) {
221 synchronized (waiter) {
222 try {
223 // notify examinerabout to wait on a monitor
224 notifyWaiter();
225
226 long base = System.nanoTime();
227 setBlockingBaseTime(base);
228 waiter.wait(ms);
229 long now = System.nanoTime();
230 addWaitTime(now - base);
231 } catch (Exception e) {
232 e.printStackTrace();
233 System.out.println("Unexpected exception.");
234 testFailed = true;
235 }
236 }
237 }
238
239 private void test() {
240 // notify examiner about to block on lockA
241 notifyWaiter();
242
243 long base = System.nanoTime();
244 setBlockingBaseTime(base);
245 synchronized (lockA) {
246 long now = System.nanoTime();
247 addBlockedEnterTime(now - base);
248
249 A(); // Expected blocked count = 1
250 }
251 E();
252 }
253 private void A() {
254 // notify examiner about to block on lockB
255 notifyWaiter();
256
257 long base = System.nanoTime();
258 setBlockingBaseTime(base);
259 synchronized (lockB) {
260 long now = System.nanoTime();
261 addBlockedEnterTime(now - base);
262
263 B(); // Expected blocked count = 2
264 }
265 }
266 private void B() {
267 // notify examiner about to block on lockC
268 notifyWaiter();
269
270 long base = System.nanoTime();
271 setBlockingBaseTime(base);
272 synchronized (lockC) {
273 long now = System.nanoTime();
274 addBlockedEnterTime(now - base);
275
276 C(); // Expected blocked count = 3
277 }
278 }
279 private void C() {
280 // notify examiner about to block on lockD
281 notifyWaiter();
282
283 long base = System.nanoTime();
284 setBlockingBaseTime(base);
285 synchronized (lockD) {
286 long now = System.nanoTime();
287 addBlockedEnterTime(now - base);
288
289 D(); // Expected blocked count = 4
290 }
291 }
292 private void D() {
293 goSleep(50);
294 }
295 private void E() {
296 final int WAIT = 1000;
297 waitObj(WAIT);
298 waitObj(WAIT);
299 waitObj(WAIT);
300 }
301
302 public void run() {
303 test();
304 } // run()
305 } // BlockedThread
306
307 static int blockedCount = 0;
308 static int waitedCount = 0;
309 static class Examiner extends StatThread {
310 private BlockedThread blockedThread;
311 private Semaphore semaphore = new Semaphore();
312
313 Examiner(String name) {
314 super(name);
315 }
316
317 public void setThread(BlockedThread thread) {
318 blockedThread = thread;
319 }
320
321 private void blockedTimeRangeCheck(StatThread t,
322 long blockedTime,
323 long nowNano)
324 throws Exception {
325 long expected = t.totalBlockedTimeMs(nowNano);
326
327 // accept 5% range
328 timeRangeCheck(blockedTime, expected, 5);
329 }
330 private void waitedTimeRangeCheck(StatThread t,
331 long waitedTime,
332 long nowNano)
333 throws Exception {
334 long expected = t.totalWaitTimeMs(nowNano);
335
336 // accept 5% range
337 timeRangeCheck(waitedTime, expected, 5);
338 }
339
340 private void timeRangeCheck(long time, long expected, int percent)
341 throws Exception {
342
343 double diff = expected - time;
344
345 if (trace) {
346 System.out.println(" Time = " + time +
347 " expected = " + expected +
348 ". Diff = " + diff);
349
350 }
351 // throw an exception if blockedTime and expectedTime
352 // differs > percent%
353 if (diff < 0) {
354 diff = diff * -1;
355 }
356
357 long range = (expected * percent) / 100;
358 // minimum range = 2 ms
359 if (range < 2) {
360 range = 2;
361 }
362 if (diff > range) {
363 throw new RuntimeException("TEST FAILED: " +
364 "Time returned = " + time +
365 " expected = " + expected + ". Diff = " + diff);
366 }
367 }
368 private void checkInfo(StatThread t, Thread.State s, Object lock,
369 String lockName, int bcount, int wcount)
370 throws Exception {
371
372 String action = "ERROR";
373 if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) {
374 action = "wait on ";
375 } else if (s == Thread.State.BLOCKED) {
376 action = "block on ";
377 }
378 System.out.println(t + " expected to " + action + lockName +
379 " with blocked count = " + bcount +
380 " and waited count = " + wcount);
381
382 long now = System.nanoTime();
383 ThreadInfo info = mbean.getThreadInfo(t.getId());
384 if (info.getThreadState() != s) {
385 printStack(t, info.getStackTrace());
386 throw new RuntimeException("TEST FAILED: " +
387 "Thread state returned is " + info.getThreadState() +
388 ". Expected to be " + s);
389 }
390
391 if (info.getLockName() == null ||
392 !info.getLockName().equals(lock.toString())) {
393 throw new RuntimeException("TEST FAILED: " +
394 "getLockName() returned " + info.getLockName() +
395 ". Expected to be " + lockName + " - " + lock.toString());
396 }
397
398 if (info.getBlockedCount() != bcount) {
399 throw new RuntimeException("TEST FAILED: " +
400 "Blocked Count returned is " + info.getBlockedCount() +
401 ". Expected to be " + bcount);
402 }
403 if (info.getWaitedCount() != wcount) {
404 throw new RuntimeException("TEST FAILED: " +
405 "Waited Count returned is " + info.getWaitedCount() +
406 ". Expected to be " + wcount);
407 }
408
409 String lockObj = info.getLockName();
410 if (lockObj == null || !lockObj.equals(lock.toString())) {
411 throw new RuntimeException("TEST FAILED: " +
412 "Object blocked on is " + lockObj +
413 ". Expected to be " + lock.toString());
414 }
415
416 if (!blockedTimeCheck) {
417 return;
418 }
419 long blockedTime = info.getBlockedTime();
420 if (blockedTime < 0) {
421 throw new RuntimeException("TEST FAILED: " +
422 "Blocked time returned is negative = " + blockedTime);
423 }
424
425 if (s == Thread.State.BLOCKED) {
426 blockedTimeRangeCheck(t, blockedTime, now);
427 } else {
428 timeRangeCheck(blockedTime, t.totalBlockedTimeMs(), 5);
429 }
430
431 long waitedTime = info.getWaitedTime();
432 if (waitedTime < 0) {
433 throw new RuntimeException("TEST FAILED: " +
434 "Waited time returned is negative = " + waitedTime);
435 }
436 if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) {
437 waitedTimeRangeCheck(t, waitedTime, now);
438 } else {
439 timeRangeCheck(waitedTime, t.totalWaitTimeMs(), 5);
440 }
441
442 }
443
444 private void examine() {
445 try {
446 synchronized (lockD) {
447 synchronized (lockC) {
448 synchronized (lockB) {
449 synchronized (lockA) {
450 // notify main thread to continue
451 semaphore.semaV();
452
453 // wait until BlockedThread has started
454 blockedThread.waitUntilBlocked();
455
456 blockedCount++;
457 checkInfo(blockedThread, Thread.State.BLOCKED,
458 lockA, "lockA",
459 blockedCount, waitedCount);
460 }
461
462 // wait until BlockedThread to block on lockB
463 blockedThread.waitUntilBlocked();
464
465 blockedCount++;
466 checkInfo(blockedThread, Thread.State.BLOCKED,
467 lockB, "lockB",
468 blockedCount, waitedCount);
469 }
470
471 // wait until BlockedThread to block on lockC
472 blockedThread.waitUntilBlocked();
473
474 blockedCount++;
475 checkInfo(blockedThread, Thread.State.BLOCKED,
476 lockC, "lockC",
477 blockedCount, waitedCount);
478 }
479 // wait until BlockedThread to block on lockD
480 blockedThread.waitUntilBlocked();
481 blockedCount++;
482
483 checkInfo(blockedThread, Thread.State.BLOCKED,
484 lockD, "lockD",
485 blockedCount, waitedCount);
486 }
487
488 // wait until BlockedThread about to call E()
489 // BlockedThread will wait on waiter for 3 times
490 blockedThread.waitUntilWaiting();
491
492 waitedCount++;
493 checkInfo(blockedThread, Thread.State.TIMED_WAITING,
494 waiter, "waiter", blockedCount, waitedCount);
495
496 blockedThread.waitUntilWaiting();
497
498 waitedCount++;
499 checkInfo(blockedThread, Thread.State.TIMED_WAITING,
500 waiter, "waiter", blockedCount, waitedCount);
501
502 blockedThread.waitUntilWaiting();
503
504 waitedCount++;
505 checkInfo(blockedThread, Thread.State.TIMED_WAITING,
506 waiter, "waiter", blockedCount, waitedCount);
507
508 } catch (Exception e) {
509 e.printStackTrace();
510 System.out.println("Unexpected exception.");
511 testFailed = true;
512 }
513 }
514
515 public void run() {
516 examine();
517 } // run()
518
519 public void waitUntilWaiting() {
520 semaphore.semaP();
521
522 // wait until the examiner is waiting for
523 while (!blockedThread.hasWaitersForBlocked()) {
524 goSleep(50);
525 }
526 // give a chance for the examiner thread to really wait
527 goSleep(20);
528
529 }
530 } // Examiner
531}