0N/A/*
2362N/A * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * @test
0N/A * @bug 4530538
0N/A * @summary Basic unit test of the synchronization statistics support:
0N/A *
0N/A * @author Mandy Chung
0N/A *
0N/A * @ignore 6309226
0N/A * @build Semaphore
0N/A * @run main SynchronizationStatistics
0N/A */
0N/A
0N/Aimport java.lang.management.*;
0N/A
0N/Apublic class SynchronizationStatistics {
0N/A private static ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
0N/A
0N/A private static boolean blockedTimeCheck =
0N/A mbean.isThreadContentionMonitoringSupported();
0N/A private static boolean trace = false;
0N/A
0N/A private static Object lockA = new Object();
0N/A private static Object lockB = new Object();
0N/A private static Object lockC = new Object();
0N/A private static Object lockD = new Object();
0N/A private static Object waiter = new Object();
0N/A private static volatile boolean testFailed = false;
0N/A
0N/A private static Object go = new Object();
0N/A
0N/A private static void goSleep(long ms) {
0N/A try {
0N/A Thread.sleep(ms);
0N/A } catch (InterruptedException e) {
0N/A e.printStackTrace();
0N/A System.out.println("Unexpected exception.");
0N/A testFailed = true;
0N/A }
0N/A }
0N/A
0N/A public static void main(String args[]) throws Exception {
0N/A if (args.length > 0 && args[0].equals("trace")) {
0N/A trace = true;
0N/A }
0N/A
0N/A if (blockedTimeCheck) {
0N/A mbean.setThreadContentionMonitoringEnabled(true);
0N/A }
0N/A
0N/A if (!mbean.isThreadContentionMonitoringEnabled()) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Thread Contention Monitoring is not enabled");
0N/A }
0N/A
0N/A Examiner examiner = new Examiner("Examiner");
0N/A BlockedThread blocked = new BlockedThread("BlockedThread");
0N/A examiner.setThread(blocked);
0N/A
0N/A // Start the threads and check them in Blocked and Waiting states
0N/A examiner.start();
0N/A
0N/A // wait until the examiner acquires all the locks and waiting
0N/A // for the BlockedThread to start
0N/A examiner.waitUntilWaiting();
0N/A
0N/A System.out.println("Checking the thread state for the examiner thread " +
0N/A "is waiting to begin.");
0N/A
0N/A // The Examiner should be waiting to be notified by the BlockedThread
0N/A checkThreadState(examiner, Thread.State.WAITING);
0N/A
0N/A System.out.println("Now starting the blocked thread");
0N/A blocked.start();
0N/A
0N/A try {
0N/A examiner.join();
0N/A blocked.join();
0N/A } catch (InterruptedException e) {
0N/A e.printStackTrace();
0N/A System.out.println("Unexpected exception.");
0N/A testFailed = true;
0N/A }
0N/A
0N/A if (testFailed)
0N/A throw new RuntimeException("TEST FAILED.");
0N/A
0N/A System.out.println("Test passed.");
0N/A }
0N/A
0N/A private static String INDENT = " ";
0N/A private static void printStack(Thread t, StackTraceElement[] stack) {
0N/A System.out.println(INDENT + t +
0N/A " stack: (length = " + stack.length + ")");
0N/A if (t != null) {
0N/A for (int j = 0; j < stack.length; j++) {
0N/A System.out.println(INDENT + stack[j]);
0N/A }
0N/A System.out.println();
0N/A }
0N/A }
0N/A
0N/A private static void checkThreadState(Thread thread, Thread.State s)
0N/A throws Exception {
0N/A
0N/A ThreadInfo ti = mbean.getThreadInfo(thread.getId());
0N/A if (ti.getThreadState() != s) {
0N/A ThreadInfo info = mbean.getThreadInfo(thread.getId(),
0N/A Integer.MAX_VALUE);
0N/A System.out.println(INDENT + "TEST FAILED:");
0N/A printStack(thread, info.getStackTrace());
0N/A System.out.println(INDENT + "Thread state: " + info.getThreadState());
0N/A
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Thread state for " + thread + " returns " + ti.getThreadState() +
0N/A ". Expected to be " + s);
0N/A }
0N/A }
0N/A
0N/A private static void checkThreadState(Thread thread,
0N/A Thread.State s1, Thread.State s2)
0N/A throws Exception {
0N/A
0N/A ThreadInfo ti = mbean.getThreadInfo(thread.getId());
0N/A if (ti.getThreadState() != s1 && ti.getThreadState() != s2) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Thread state for " + thread + " returns " + ti.getThreadState() +
0N/A ". Expected to be " + s1 + " or " + s2);
0N/A }
0N/A }
0N/A
0N/A static class StatThread extends Thread {
0N/A private long blockingBaseTime = 0;
0N/A private long totalWaitTime = 0;
0N/A private long totalBlockedEnterTime = 0;
0N/A
0N/A StatThread(String name) {
0N/A super(name);
0N/A }
0N/A
0N/A void addWaitTime(long ns) {
0N/A totalWaitTime = totalWaitTime + ns;
0N/A }
0N/A void addBlockedEnterTime(long ns) {
0N/A totalBlockedEnterTime = totalBlockedEnterTime + ns;
0N/A }
0N/A void setBlockingBaseTime(long time) {
0N/A blockingBaseTime = time;
0N/A }
0N/A
0N/A long totalBlockedTimeMs() {
0N/A return totalBlockedEnterTime / 1000000;
0N/A }
0N/A
0N/A long totalBlockedTimeMs(long now) {
0N/A long t = totalBlockedEnterTime + (now - blockingBaseTime);
0N/A return t / 1000000;
0N/A }
0N/A
0N/A long totalWaitTimeMs() {
0N/A return totalWaitTime / 1000000;
0N/A }
0N/A
0N/A long totalWaitTimeMs(long now) {
0N/A long t = totalWaitTime + (now - blockingBaseTime);
0N/A return t / 1000000;
0N/A }
0N/A }
0N/A
0N/A static class BlockedThread extends StatThread {
0N/A private Semaphore handshake = new Semaphore();
0N/A BlockedThread(String name) {
0N/A super(name);
0N/A }
0N/A void waitUntilBlocked() {
0N/A handshake.semaP();
0N/A
0N/A // give a chance for the examiner thread to really wait
0N/A goSleep(20);
0N/A }
0N/A
0N/A void waitUntilWaiting() {
0N/A waitUntilBlocked();
0N/A }
0N/A
0N/A boolean hasWaitersForBlocked() {
0N/A return (handshake.getWaiterCount() > 0);
0N/A }
0N/A
0N/A private void notifyWaiter() {
0N/A // wait until the examiner waits on the semaphore
0N/A while (handshake.getWaiterCount() == 0) {
0N/A goSleep(20);
0N/A }
0N/A handshake.semaV();
0N/A }
0N/A
0N/A private void waitObj(long ms) {
0N/A synchronized (waiter) {
0N/A try {
0N/A // notify examinerabout to wait on a monitor
0N/A notifyWaiter();
0N/A
0N/A long base = System.nanoTime();
0N/A setBlockingBaseTime(base);
0N/A waiter.wait(ms);
0N/A long now = System.nanoTime();
0N/A addWaitTime(now - base);
0N/A } catch (Exception e) {
0N/A e.printStackTrace();
0N/A System.out.println("Unexpected exception.");
0N/A testFailed = true;
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void test() {
0N/A // notify examiner about to block on lockA
0N/A notifyWaiter();
0N/A
0N/A long base = System.nanoTime();
0N/A setBlockingBaseTime(base);
0N/A synchronized (lockA) {
0N/A long now = System.nanoTime();
0N/A addBlockedEnterTime(now - base);
0N/A
0N/A A(); // Expected blocked count = 1
0N/A }
0N/A E();
0N/A }
0N/A private void A() {
0N/A // notify examiner about to block on lockB
0N/A notifyWaiter();
0N/A
0N/A long base = System.nanoTime();
0N/A setBlockingBaseTime(base);
0N/A synchronized (lockB) {
0N/A long now = System.nanoTime();
0N/A addBlockedEnterTime(now - base);
0N/A
0N/A B(); // Expected blocked count = 2
0N/A }
0N/A }
0N/A private void B() {
0N/A // notify examiner about to block on lockC
0N/A notifyWaiter();
0N/A
0N/A long base = System.nanoTime();
0N/A setBlockingBaseTime(base);
0N/A synchronized (lockC) {
0N/A long now = System.nanoTime();
0N/A addBlockedEnterTime(now - base);
0N/A
0N/A C(); // Expected blocked count = 3
0N/A }
0N/A }
0N/A private void C() {
0N/A // notify examiner about to block on lockD
0N/A notifyWaiter();
0N/A
0N/A long base = System.nanoTime();
0N/A setBlockingBaseTime(base);
0N/A synchronized (lockD) {
0N/A long now = System.nanoTime();
0N/A addBlockedEnterTime(now - base);
0N/A
0N/A D(); // Expected blocked count = 4
0N/A }
0N/A }
0N/A private void D() {
0N/A goSleep(50);
0N/A }
0N/A private void E() {
0N/A final int WAIT = 1000;
0N/A waitObj(WAIT);
0N/A waitObj(WAIT);
0N/A waitObj(WAIT);
0N/A }
0N/A
0N/A public void run() {
0N/A test();
0N/A } // run()
0N/A } // BlockedThread
0N/A
0N/A static int blockedCount = 0;
0N/A static int waitedCount = 0;
0N/A static class Examiner extends StatThread {
0N/A private BlockedThread blockedThread;
0N/A private Semaphore semaphore = new Semaphore();
0N/A
0N/A Examiner(String name) {
0N/A super(name);
0N/A }
0N/A
0N/A public void setThread(BlockedThread thread) {
0N/A blockedThread = thread;
0N/A }
0N/A
0N/A private void blockedTimeRangeCheck(StatThread t,
0N/A long blockedTime,
0N/A long nowNano)
0N/A throws Exception {
0N/A long expected = t.totalBlockedTimeMs(nowNano);
0N/A
0N/A // accept 5% range
0N/A timeRangeCheck(blockedTime, expected, 5);
0N/A }
0N/A private void waitedTimeRangeCheck(StatThread t,
0N/A long waitedTime,
0N/A long nowNano)
0N/A throws Exception {
0N/A long expected = t.totalWaitTimeMs(nowNano);
0N/A
0N/A // accept 5% range
0N/A timeRangeCheck(waitedTime, expected, 5);
0N/A }
0N/A
0N/A private void timeRangeCheck(long time, long expected, int percent)
0N/A throws Exception {
0N/A
0N/A double diff = expected - time;
0N/A
0N/A if (trace) {
0N/A System.out.println(" Time = " + time +
0N/A " expected = " + expected +
0N/A ". Diff = " + diff);
0N/A
0N/A }
0N/A // throw an exception if blockedTime and expectedTime
0N/A // differs > percent%
0N/A if (diff < 0) {
0N/A diff = diff * -1;
0N/A }
0N/A
0N/A long range = (expected * percent) / 100;
0N/A // minimum range = 2 ms
0N/A if (range < 2) {
0N/A range = 2;
0N/A }
0N/A if (diff > range) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Time returned = " + time +
0N/A " expected = " + expected + ". Diff = " + diff);
0N/A }
0N/A }
0N/A private void checkInfo(StatThread t, Thread.State s, Object lock,
0N/A String lockName, int bcount, int wcount)
0N/A throws Exception {
0N/A
0N/A String action = "ERROR";
0N/A if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) {
0N/A action = "wait on ";
0N/A } else if (s == Thread.State.BLOCKED) {
0N/A action = "block on ";
0N/A }
0N/A System.out.println(t + " expected to " + action + lockName +
0N/A " with blocked count = " + bcount +
0N/A " and waited count = " + wcount);
0N/A
0N/A long now = System.nanoTime();
0N/A ThreadInfo info = mbean.getThreadInfo(t.getId());
0N/A if (info.getThreadState() != s) {
0N/A printStack(t, info.getStackTrace());
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Thread state returned is " + info.getThreadState() +
0N/A ". Expected to be " + s);
0N/A }
0N/A
0N/A if (info.getLockName() == null ||
0N/A !info.getLockName().equals(lock.toString())) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "getLockName() returned " + info.getLockName() +
0N/A ". Expected to be " + lockName + " - " + lock.toString());
0N/A }
0N/A
0N/A if (info.getBlockedCount() != bcount) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Blocked Count returned is " + info.getBlockedCount() +
0N/A ". Expected to be " + bcount);
0N/A }
0N/A if (info.getWaitedCount() != wcount) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Waited Count returned is " + info.getWaitedCount() +
0N/A ". Expected to be " + wcount);
0N/A }
0N/A
0N/A String lockObj = info.getLockName();
0N/A if (lockObj == null || !lockObj.equals(lock.toString())) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Object blocked on is " + lockObj +
0N/A ". Expected to be " + lock.toString());
0N/A }
0N/A
0N/A if (!blockedTimeCheck) {
0N/A return;
0N/A }
0N/A long blockedTime = info.getBlockedTime();
0N/A if (blockedTime < 0) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Blocked time returned is negative = " + blockedTime);
0N/A }
0N/A
0N/A if (s == Thread.State.BLOCKED) {
0N/A blockedTimeRangeCheck(t, blockedTime, now);
0N/A } else {
0N/A timeRangeCheck(blockedTime, t.totalBlockedTimeMs(), 5);
0N/A }
0N/A
0N/A long waitedTime = info.getWaitedTime();
0N/A if (waitedTime < 0) {
0N/A throw new RuntimeException("TEST FAILED: " +
0N/A "Waited time returned is negative = " + waitedTime);
0N/A }
0N/A if (s == Thread.State.WAITING || s == Thread.State.TIMED_WAITING) {
0N/A waitedTimeRangeCheck(t, waitedTime, now);
0N/A } else {
0N/A timeRangeCheck(waitedTime, t.totalWaitTimeMs(), 5);
0N/A }
0N/A
0N/A }
0N/A
0N/A private void examine() {
0N/A try {
0N/A synchronized (lockD) {
0N/A synchronized (lockC) {
0N/A synchronized (lockB) {
0N/A synchronized (lockA) {
0N/A // notify main thread to continue
0N/A semaphore.semaV();
0N/A
0N/A // wait until BlockedThread has started
0N/A blockedThread.waitUntilBlocked();
0N/A
0N/A blockedCount++;
0N/A checkInfo(blockedThread, Thread.State.BLOCKED,
0N/A lockA, "lockA",
0N/A blockedCount, waitedCount);
0N/A }
0N/A
0N/A // wait until BlockedThread to block on lockB
0N/A blockedThread.waitUntilBlocked();
0N/A
0N/A blockedCount++;
0N/A checkInfo(blockedThread, Thread.State.BLOCKED,
0N/A lockB, "lockB",
0N/A blockedCount, waitedCount);
0N/A }
0N/A
0N/A // wait until BlockedThread to block on lockC
0N/A blockedThread.waitUntilBlocked();
0N/A
0N/A blockedCount++;
0N/A checkInfo(blockedThread, Thread.State.BLOCKED,
0N/A lockC, "lockC",
0N/A blockedCount, waitedCount);
0N/A }
0N/A // wait until BlockedThread to block on lockD
0N/A blockedThread.waitUntilBlocked();
0N/A blockedCount++;
0N/A
0N/A checkInfo(blockedThread, Thread.State.BLOCKED,
0N/A lockD, "lockD",
0N/A blockedCount, waitedCount);
0N/A }
0N/A
0N/A // wait until BlockedThread about to call E()
0N/A // BlockedThread will wait on waiter for 3 times
0N/A blockedThread.waitUntilWaiting();
0N/A
0N/A waitedCount++;
0N/A checkInfo(blockedThread, Thread.State.TIMED_WAITING,
0N/A waiter, "waiter", blockedCount, waitedCount);
0N/A
0N/A blockedThread.waitUntilWaiting();
0N/A
0N/A waitedCount++;
0N/A checkInfo(blockedThread, Thread.State.TIMED_WAITING,
0N/A waiter, "waiter", blockedCount, waitedCount);
0N/A
0N/A blockedThread.waitUntilWaiting();
0N/A
0N/A waitedCount++;
0N/A checkInfo(blockedThread, Thread.State.TIMED_WAITING,
0N/A waiter, "waiter", blockedCount, waitedCount);
0N/A
0N/A } catch (Exception e) {
0N/A e.printStackTrace();
0N/A System.out.println("Unexpected exception.");
0N/A testFailed = true;
0N/A }
0N/A }
0N/A
0N/A public void run() {
0N/A examine();
0N/A } // run()
0N/A
0N/A public void waitUntilWaiting() {
0N/A semaphore.semaP();
0N/A
0N/A // wait until the examiner is waiting for
0N/A while (!blockedThread.hasWaitersForBlocked()) {
0N/A goSleep(50);
0N/A }
0N/A // give a chance for the examiner thread to really wait
0N/A goSleep(20);
0N/A
0N/A }
0N/A } // Examiner
0N/A}