提交 a359dcc0 编写于 作者: J jbachorik

6309226: TEST:...

6309226: TEST: java/lang/management/ThreadMXBean/SynchronizationStatistics.java didn't check Thread.sleep
Reviewed-by: dholmes, mchung
上级 31b3aa07
/* /*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -27,45 +27,28 @@ ...@@ -27,45 +27,28 @@
* @summary Basic unit test of the synchronization statistics support: * @summary Basic unit test of the synchronization statistics support:
* *
* @author Mandy Chung * @author Mandy Chung
* @author Jaroslav Bachorik
* *
* @ignore 6309226
* @build Semaphore
* @run main/othervm SynchronizationStatistics * @run main/othervm SynchronizationStatistics
*/ */
import java.lang.management.*; import java.lang.management.*;
import java.util.concurrent.Phaser;
public class SynchronizationStatistics { public class SynchronizationStatistics {
private static ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); private static class LockerThread extends Thread {
public LockerThread(Runnable r) {
private static boolean blockedTimeCheck = super(r, "LockerThread");
mbean.isThreadContentionMonitoringSupported();
private static boolean trace = false;
private static Object lockA = new Object();
private static Object lockB = new Object();
private static Object lockC = new Object();
private static Object lockD = new Object();
private static Object waiter = new Object();
private static volatile boolean testFailed = false;
private static Object go = new Object();
private static void goSleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Unexpected exception.");
testFailed = true;
} }
} }
public static void main(String args[]) throws Exception { private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
if (args.length > 0 && args[0].equals("trace")) {
trace = true;
}
private static final boolean blockedTimeCheck =
mbean.isThreadContentionMonitoringSupported();
public static void main(String args[]) throws Exception {
if (blockedTimeCheck) { if (blockedTimeCheck) {
mbean.setThreadContentionMonitoringEnabled(true); mbean.setThreadContentionMonitoringEnabled(true);
} }
...@@ -75,457 +58,317 @@ public class SynchronizationStatistics { ...@@ -75,457 +58,317 @@ public class SynchronizationStatistics {
"Thread Contention Monitoring is not enabled"); "Thread Contention Monitoring is not enabled");
} }
Examiner examiner = new Examiner("Examiner"); testBlockingOnSimpleMonitor();
BlockedThread blocked = new BlockedThread("BlockedThread"); testBlockingOnNestedMonitor();
examiner.setThread(blocked); testWaitingOnSimpleMonitor();
testMultiWaitingOnSimpleMonitor();
// Start the threads and check them in Blocked and Waiting states testWaitingOnNestedMonitor();
examiner.start();
// wait until the examiner acquires all the locks and waiting
// for the BlockedThread to start
examiner.waitUntilWaiting();
System.out.println("Checking the thread state for the examiner thread " +
"is waiting to begin.");
// The Examiner should be waiting to be notified by the BlockedThread
checkThreadState(examiner, Thread.State.WAITING);
System.out.println("Now starting the blocked thread");
blocked.start();
try {
examiner.join();
blocked.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Unexpected exception.");
testFailed = true;
}
if (testFailed)
throw new RuntimeException("TEST FAILED.");
System.out.println("Test passed."); System.out.println("Test passed.");
} }
private static String INDENT = " "; private static LockerThread newLockerThread(Runnable r) {
private static void printStack(Thread t, StackTraceElement[] stack) { LockerThread t = new LockerThread(r);
System.out.println(INDENT + t + t.setDaemon(true);
" stack: (length = " + stack.length + ")"); return t;
if (t != null) {
for (int j = 0; j < stack.length; j++) {
System.out.println(INDENT + stack[j]);
}
System.out.println();
}
} }
private static void checkThreadState(Thread thread, Thread.State s) private static void waitForThreadState(Thread t, Thread.State state) throws InterruptedException {
throws Exception { while (!t.isInterrupted() && t.getState() != state) {
Thread.sleep(3);
ThreadInfo ti = mbean.getThreadInfo(thread.getId());
if (ti.getThreadState() != s) {
ThreadInfo info = mbean.getThreadInfo(thread.getId(),
Integer.MAX_VALUE);
System.out.println(INDENT + "TEST FAILED:");
printStack(thread, info.getStackTrace());
System.out.println(INDENT + "Thread state: " + info.getThreadState());
throw new RuntimeException("TEST FAILED: " +
"Thread state for " + thread + " returns " + ti.getThreadState() +
". Expected to be " + s);
} }
} }
private static void checkThreadState(Thread thread, /**
Thread.State s1, Thread.State s2) * Tests that blocking on a single monitor properly increases the
throws Exception { * blocked count at least by 1. Also asserts that the correct lock name is provided.
*/
ThreadInfo ti = mbean.getThreadInfo(thread.getId()); private static void testBlockingOnSimpleMonitor() throws Exception {
if (ti.getThreadState() != s1 && ti.getThreadState() != s2) { System.out.println("testBlockingOnSimpleMonitor");
throw new RuntimeException("TEST FAILED: " + final Object lock1 = new Object();
"Thread state for " + thread + " returns " + ti.getThreadState() + final Phaser p = new Phaser(2);
". Expected to be " + s1 + " or " + s2); LockerThread lt = newLockerThread(new Runnable() {
} @Override
} public void run() {
p.arriveAndAwaitAdvance(); // phase[1]
static class StatThread extends Thread { synchronized(lock1) {
private long blockingBaseTime = 0; System.out.println("[LockerThread obtained Lock1]");
private long totalWaitTime = 0; p.arriveAndAwaitAdvance(); // phase[2]
private long totalBlockedEnterTime = 0; }
p.arriveAndAwaitAdvance(); // phase[3]
StatThread(String name) { }
super(name); });
}
void addWaitTime(long ns) {
totalWaitTime = totalWaitTime + ns;
}
void addBlockedEnterTime(long ns) {
totalBlockedEnterTime = totalBlockedEnterTime + ns;
}
void setBlockingBaseTime(long time) {
blockingBaseTime = time;
}
long totalBlockedTimeMs() { lt.start();
return totalBlockedEnterTime / 1000000; long tid = lt.getId();
ThreadInfo ti = mbean.getThreadInfo(tid);
String lockName = null;
synchronized(lock1) {
p.arriveAndAwaitAdvance(); // phase[1]
waitForThreadState(lt, Thread.State.BLOCKED);
lockName = mbean.getThreadInfo(tid).getLockName();
} }
long totalBlockedTimeMs(long now) { p.arriveAndAwaitAdvance(); // phase[2]
long t = totalBlockedEnterTime + (now - blockingBaseTime); testBlocked(ti, mbean.getThreadInfo(tid), lockName, lock1);
return t / 1000000; p.arriveAndDeregister(); // phase[3]
}
long totalWaitTimeMs() { lt.join();
return totalWaitTime / 1000000;
}
long totalWaitTimeMs(long now) { System.out.println("OK");
long t = totalWaitTime + (now - blockingBaseTime);
return t / 1000000;
}
} }
static class BlockedThread extends StatThread { /**
private Semaphore handshake = new Semaphore(); * Tests that blocking on a nested monitor properly increases the
BlockedThread(String name) { * blocked count at least by 1 - it is not affected by the nesting depth.
super(name); * Also asserts that the correct lock name is provided.
} */
void waitUntilBlocked() { private static void testBlockingOnNestedMonitor() throws Exception {
handshake.semaP(); System.out.println("testBlockingOnNestedMonitor");
final Object lock1 = new Object();
// give a chance for the examiner thread to really wait final Object lock2 = new Object();
goSleep(20);
} final Phaser p = new Phaser(2);
LockerThread lt = newLockerThread(new Runnable() {
void waitUntilWaiting() { @Override
waitUntilBlocked(); public void run() {
} p.arriveAndAwaitAdvance(); // phase[1]
synchronized(lock1) {
boolean hasWaitersForBlocked() { System.out.println("[LockerThread obtained Lock1]");
return (handshake.getWaiterCount() > 0); p.arriveAndAwaitAdvance(); // phase[2]
} p.arriveAndAwaitAdvance(); // phase[3]
synchronized(lock2) {
private void notifyWaiter() { System.out.println("[LockerThread obtained Lock2]");
// wait until the examiner waits on the semaphore p.arriveAndAwaitAdvance(); // phase[4]
while (handshake.getWaiterCount() == 0) { }
goSleep(20); p.arriveAndAwaitAdvance(); // phase[5]
}
handshake.semaV();
}
private void waitObj(long ms) {
synchronized (waiter) {
try {
// notify examinerabout to wait on a monitor
notifyWaiter();
long base = System.nanoTime();
setBlockingBaseTime(base);
waiter.wait(ms);
long now = System.nanoTime();
addWaitTime(now - base);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Unexpected exception.");
testFailed = true;
} }
} }
} });
private void test() {
// notify examiner about to block on lockA
notifyWaiter();
long base = System.nanoTime();
setBlockingBaseTime(base);
synchronized (lockA) {
long now = System.nanoTime();
addBlockedEnterTime(now - base);
A(); // Expected blocked count = 1 lt.start();
} long tid = lt.getId();
E(); ThreadInfo ti = mbean.getThreadInfo(tid);
ThreadInfo ti1 = null;
String lockName = null;
synchronized(lock1) {
p.arriveAndAwaitAdvance(); // phase[1]
waitForThreadState(lt, Thread.State.BLOCKED);
lockName = mbean.getThreadInfo(tid).getLockName();
} }
private void A() { p.arriveAndAwaitAdvance(); // phase[2]
// notify examiner about to block on lockB
notifyWaiter();
long base = System.nanoTime(); ti1 = mbean.getThreadInfo(tid);
setBlockingBaseTime(base); testBlocked(ti, ti1, lockName, lock1);
synchronized (lockB) { ti = ti1;
long now = System.nanoTime();
addBlockedEnterTime(now - base);
B(); // Expected blocked count = 2 synchronized(lock2) {
} p.arriveAndAwaitAdvance(); // phase [3]
waitForThreadState(lt, Thread.State.BLOCKED);
lockName = mbean.getThreadInfo(tid).getLockName();
} }
private void B() { p.arriveAndAwaitAdvance(); // phase [4]
// notify examiner about to block on lockC testBlocked(ti, mbean.getThreadInfo(tid), lockName, lock2);
notifyWaiter(); p.arriveAndDeregister();
long base = System.nanoTime(); lt.join();
setBlockingBaseTime(base);
synchronized (lockC) {
long now = System.nanoTime();
addBlockedEnterTime(now - base);
C(); // Expected blocked count = 3 System.out.println("OK");
} }
}
private void C() {
// notify examiner about to block on lockD
notifyWaiter();
long base = System.nanoTime();
setBlockingBaseTime(base);
synchronized (lockD) {
long now = System.nanoTime();
addBlockedEnterTime(now - base);
D(); // Expected blocked count = 4 /**
* Tests that waiting on a single monitor properly increases the waited
* count by 1 and the waited time by a positive number.
*/
private static void testWaitingOnSimpleMonitor() throws Exception {
System.out.println("testWaitingOnSimpleMonitor");
final Object lock1 = new Object();
final Phaser p = new Phaser(2);
LockerThread lt = newLockerThread(new Runnable() {
@Override
public void run() {
p.arriveAndAwaitAdvance(); // phase[1]
synchronized(lock1) {
System.out.println("[LockerThread obtained Lock1]");
try {
lock1.wait(300);
} catch (InterruptedException ex) {
// ignore
}
p.arriveAndAwaitAdvance(); // phase[2]
}
p.arriveAndAwaitAdvance(); // phase[3]
} }
} });
private void D() {
goSleep(50);
}
private void E() {
final int WAIT = 1000;
waitObj(WAIT);
waitObj(WAIT);
waitObj(WAIT);
}
public void run() { lt.start();
test(); ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
} // run() synchronized(lock1) {
} // BlockedThread p.arriveAndAwaitAdvance(); // phase[1]
waitForThreadState(lt, Thread.State.BLOCKED);
static int blockedCount = 0;
static int waitedCount = 0;
static class Examiner extends StatThread {
private BlockedThread blockedThread;
private Semaphore semaphore = new Semaphore();
Examiner(String name) {
super(name);
} }
p.arriveAndAwaitAdvance(); // phase[2]
public void setThread(BlockedThread thread) { ThreadInfo ti2 = mbean.getThreadInfo(lt.getId());
blockedThread = thread; p.arriveAndDeregister(); // phase[3]
}
private void blockedTimeRangeCheck(StatThread t, lt.join();
long blockedTime,
long nowNano)
throws Exception {
long expected = t.totalBlockedTimeMs(nowNano);
// accept 5% range testWaited(ti1, ti2, 1);
timeRangeCheck(blockedTime, expected, 5); System.out.println("OK");
} }
private void waitedTimeRangeCheck(StatThread t,
long waitedTime,
long nowNano)
throws Exception {
long expected = t.totalWaitTimeMs(nowNano);
// accept 5% range
timeRangeCheck(waitedTime, expected, 5);
}
private void timeRangeCheck(long time, long expected, int percent)
throws Exception {
double diff = expected - time;
if (trace) {
System.out.println(" Time = " + time +
" expected = " + expected +
". Diff = " + diff);
/**
* Tests that waiting multiple times on the same monitor subsequently
* increases the waited count by the number of subsequent calls and the
* waited time by a positive number.
*/
private static void testMultiWaitingOnSimpleMonitor() throws Exception {
System.out.println("testWaitingOnMultipleMonitors");
final Object lock1 = new Object();
final Phaser p = new Phaser(2);
LockerThread lt = newLockerThread(new Runnable() {
@Override
public void run() {
p.arriveAndAwaitAdvance(); // phase[1]
synchronized(lock1) {
System.out.println("[LockerThread obtained Lock1]");
for (int i = 0; i < 3; i++) {
try {
lock1.wait(300);
} catch (InterruptedException ex) {
// ignore
}
p.arriveAndAwaitAdvance(); // phase[2-4]
}
}
p.arriveAndAwaitAdvance(); // phase[5]
} }
// throw an exception if blockedTime and expectedTime });
// differs > percent%
if (diff < 0) {
diff = diff * -1;
}
long range = (expected * percent) / 100; lt.start();
// minimum range = 2 ms ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
if (range < 2) { synchronized(lock1) {
range = 2; p.arriveAndAwaitAdvance(); //phase[1]
} waitForThreadState(lt, Thread.State.BLOCKED);
if (diff > range) {
throw new RuntimeException("TEST FAILED: " +
"Time returned = " + time +
" expected = " + expected + ". Diff = " + diff);
}
} }
private void checkInfo(StatThread t, Thread.State s, Object lock, int phase = p.getPhase();
String lockName, int bcount, int wcount) while ((p.arriveAndAwaitAdvance() - phase) < 3); // phase[2-4]
throws Exception {
String action = "ERROR";
if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) {
action = "wait on ";
} else if (s == Thread.State.BLOCKED) {
action = "block on ";
}
System.out.println(t + " expected to " + action + lockName +
" with blocked count = " + bcount +
" and waited count = " + wcount);
long now = System.nanoTime();
ThreadInfo info = mbean.getThreadInfo(t.getId());
if (info.getThreadState() != s) {
printStack(t, info.getStackTrace());
throw new RuntimeException("TEST FAILED: " +
"Thread state returned is " + info.getThreadState() +
". Expected to be " + s);
}
if (info.getLockName() == null ||
!info.getLockName().equals(lock.toString())) {
throw new RuntimeException("TEST FAILED: " +
"getLockName() returned " + info.getLockName() +
". Expected to be " + lockName + " - " + lock.toString());
}
if (info.getBlockedCount() != bcount) {
throw new RuntimeException("TEST FAILED: " +
"Blocked Count returned is " + info.getBlockedCount() +
". Expected to be " + bcount);
}
if (info.getWaitedCount() != wcount) {
throw new RuntimeException("TEST FAILED: " +
"Waited Count returned is " + info.getWaitedCount() +
". Expected to be " + wcount);
}
String lockObj = info.getLockName(); ThreadInfo ti2 = mbean.getThreadInfo(lt.getId());
if (lockObj == null || !lockObj.equals(lock.toString())) { p.arriveAndDeregister(); // phase[5]
throw new RuntimeException("TEST FAILED: " +
"Object blocked on is " + lockObj +
". Expected to be " + lock.toString());
}
if (!blockedTimeCheck) {
return;
}
long blockedTime = info.getBlockedTime();
if (blockedTime < 0) {
throw new RuntimeException("TEST FAILED: " +
"Blocked time returned is negative = " + blockedTime);
}
if (s == Thread.State.BLOCKED) { lt.join();
blockedTimeRangeCheck(t, blockedTime, now); testWaited(ti1, ti2, 3);
} else { System.out.println("OK");
timeRangeCheck(blockedTime, t.totalBlockedTimeMs(), 5); }
}
long waitedTime = info.getWaitedTime(); /**
if (waitedTime < 0) { * Tests that waiting on monitors places in nested synchronized blocks
throw new RuntimeException("TEST FAILED: " + * properly increases the waited count by the number of times the "lock.wait()"
"Waited time returned is negative = " + waitedTime); * was invoked and the waited time by a positive number.
} */
if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) { private static void testWaitingOnNestedMonitor() throws Exception {
waitedTimeRangeCheck(t, waitedTime, now); System.out.println("testWaitingOnNestedMonitor");
} else { final Object lock1 = new Object();
timeRangeCheck(waitedTime, t.totalWaitTimeMs(), 5); final Object lock2 = new Object();
} final Object lock3 = new Object();
final Phaser p = new Phaser(2);
LockerThread lt = newLockerThread(new Runnable() {
@Override
public void run() {
p.arriveAndAwaitAdvance(); // phase[1]
synchronized(lock1) {
System.out.println("[LockerThread obtained Lock1]");
try {
lock1.wait(300);
} catch (InterruptedException ex) {
// ignore
}
} p.arriveAndAwaitAdvance(); // phase[2]
synchronized(lock2) {
System.out.println("[LockerThread obtained Lock2]");
try {
lock2.wait(300);
} catch (InterruptedException ex) {
// ignore
}
private void examine() { p.arriveAndAwaitAdvance(); // phase[3]
try { synchronized(lock3) {
synchronized (lockD) { System.out.println("[LockerThread obtained Lock3]");
synchronized (lockC) { try {
synchronized (lockB) { lock3.wait(300);
synchronized (lockA) { } catch (InterruptedException ex) {
// notify main thread to continue // ignore
semaphore.semaV();
// wait until BlockedThread has started
blockedThread.waitUntilBlocked();
blockedCount++;
checkInfo(blockedThread, Thread.State.BLOCKED,
lockA, "lockA",
blockedCount, waitedCount);
} }
p.arriveAndAwaitAdvance(); // phase[4]
// wait until BlockedThread to block on lockB
blockedThread.waitUntilBlocked();
blockedCount++;
checkInfo(blockedThread, Thread.State.BLOCKED,
lockB, "lockB",
blockedCount, waitedCount);
} }
// wait until BlockedThread to block on lockC
blockedThread.waitUntilBlocked();
blockedCount++;
checkInfo(blockedThread, Thread.State.BLOCKED,
lockC, "lockC",
blockedCount, waitedCount);
} }
// wait until BlockedThread to block on lockD
blockedThread.waitUntilBlocked();
blockedCount++;
checkInfo(blockedThread, Thread.State.BLOCKED,
lockD, "lockD",
blockedCount, waitedCount);
} }
p.arriveAndAwaitAdvance(); // phase[5]
}
});
// wait until BlockedThread about to call E() lt.start();
// BlockedThread will wait on waiter for 3 times ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
blockedThread.waitUntilWaiting(); synchronized(lock1) {
p.arriveAndAwaitAdvance(); // phase[1]
waitedCount++; waitForThreadState(lt, Thread.State.BLOCKED);
checkInfo(blockedThread, Thread.State.TIMED_WAITING, }
waiter, "waiter", blockedCount, waitedCount);
blockedThread.waitUntilWaiting();
waitedCount++;
checkInfo(blockedThread, Thread.State.TIMED_WAITING,
waiter, "waiter", blockedCount, waitedCount);
blockedThread.waitUntilWaiting();
waitedCount++; synchronized(lock2) {
checkInfo(blockedThread, Thread.State.TIMED_WAITING, p.arriveAndAwaitAdvance(); // phase[2]
waiter, "waiter", blockedCount, waitedCount); waitForThreadState(lt, Thread.State.BLOCKED);
}
} catch (Exception e) { synchronized(lock3) {
e.printStackTrace(); p.arriveAndAwaitAdvance(); // phase[3]
System.out.println("Unexpected exception."); waitForThreadState(lt, Thread.State.BLOCKED);
testFailed = true;
}
} }
public void run() { p.arriveAndAwaitAdvance(); // phase[4]
examine(); ThreadInfo ti2 = mbean.getThreadInfo(lt.getId());
} // run() p.arriveAndDeregister(); // phase[5]
public void waitUntilWaiting() { lt.join();
semaphore.semaP(); testWaited(ti1, ti2, 3);
System.out.println("OK");
}
// wait until the examiner is waiting for private static void testWaited(ThreadInfo ti1, ThreadInfo ti2, int waited) throws Error {
while (!blockedThread.hasWaitersForBlocked()) { long waitCntDiff = ti2.getWaitedCount() - ti1.getWaitedCount();
goSleep(50); long waitTimeDiff = ti2.getWaitedTime() - ti1.getWaitedTime();
} if (waitCntDiff < waited) {
// give a chance for the examiner thread to really wait throw new Error("Unexpected diff in waited count. Expecting at least "
goSleep(20); + waited + " , got " + waitCntDiff);
}
if (waitTimeDiff <= 0) {
throw new Error("Unexpected diff in waited time. Expecting increasing " +
"value, got " + waitTimeDiff + "ms");
}
}
private static void testBlocked(ThreadInfo ti1, ThreadInfo ti2,
String lockName, final Object lock)
throws Error {
long blkCntDiff = ti2.getBlockedCount() - ti1.getBlockedCount();
long blkTimeDiff = ti2.getBlockedTime() - ti1.getBlockedTime();
if (blkCntDiff < 1) {
throw new Error("Unexpected diff in blocked count. Expecting at least 1, " +
"got " + blkCntDiff);
} }
} // Examiner if (blkTimeDiff < 0) {
throw new Error("Unexpected diff in blocked time. Expecting a positive " +
"number, got " + blkTimeDiff);
}
if (!lockName.equals(lock.toString())) {
throw new Error("Unexpected blocked monitor name. Expecting " +
lock.toString() + ", got " +
lockName);
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册