# HG changeset patch # User jbachorik # Date 1391013460 -3600 # Node ID 92f9ded70a69eb063b75f8ecb066ad0b47e50510 # Parent 4dd8f4f14e2ce8fc9c6e07896341166c2552ee72 8031701: java/lang/management/ThreadMXBean/Locks.java: Thread WaitingThread is expected to wait on Object but got null Thread.State = RUNNABLE Reviewed-by: mchung, dsamersoff diff -r 4dd8f4f14e2c -r 92f9ded70a69 jdk/test/java/lang/management/ThreadMXBean/Locks.java --- a/jdk/test/java/lang/management/ThreadMXBean/Locks.java Wed Jan 29 13:10:53 2014 +0100 +++ b/jdk/test/java/lang/management/ThreadMXBean/Locks.java Wed Jan 29 17:37:40 2014 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, 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. * * This code is free software; you can redistribute it and/or modify it @@ -27,18 +27,19 @@ * @summary Basic unit test of ThreadInfo.getLockName() * and ThreadInfo.getLockOwnerName() * @author Mandy Chung + * @author Jaroslav Bachorik * - * @build ThreadExecutionSynchronizer * @run main/othervm Locks */ import java.lang.management.*; +import java.util.concurrent.Phaser; public class Locks { - private static Object objA = new Object(); - private static Object objB = new Object(); - private static Object objC = new Object(); - private static ThreadMXBean tm = ManagementFactory.getThreadMXBean(); + private static final Object objA = new Object(); + private static final Object objB = new Object(); + private static final Object objC = new Object(); + private static final ThreadMXBean tm = ManagementFactory.getThreadMXBean(); private static boolean testFailed = false; @@ -46,48 +47,62 @@ if (lock == null) return null; return lock.getClass().getName() + '@' + - Integer.toHexString(System.identityHashCode(lock)); + Integer.toHexString(System.identityHashCode(lock)); + } + + private static void assertNoLock(Thread t) { + long tid = t.getId(); + ThreadInfo info = tm.getThreadInfo(tid); + String result = info.getLockName(); + + if (result != null) { + throw new RuntimeException("Thread " + t.getName() + " is not supposed to hold any lock. " + + "Currently owning lock: " + result); + } } private static void checkBlockedObject(Thread t, Object lock, Thread owner, Thread.State expectedState) { - ThreadInfo info = tm.getThreadInfo(t.getId()); + long tid = t.getId(); + ThreadInfo info = tm.getThreadInfo(tid); String result = info.getLockName(); String expectedLock = (lock != null ? getLockName(lock) : null); String expectedOwner = (owner != null ? owner.getName() : null); if (lock != null) { - if (expectedState ==Thread.State.BLOCKED) { + if (expectedState == Thread.State.BLOCKED) { int retryCount=0; while(info.getThreadState() != Thread.State.BLOCKED) { if (retryCount++ > 500) { throw new RuntimeException("Thread " + t.getName() + - " is expected to block on " + expectedLock + - " but got " + result + - " Thread.State = " + info.getThreadState()); + " is expected to block on " + expectedLock + + " but got " + result + + " Thread.State = " + info.getThreadState()); } goSleep(100); + info = tm.getThreadInfo(tid); + result = info.getLockName(); } } if (expectedState == Thread.State.WAITING && - info.getThreadState() != Thread.State.WAITING) { + info.getThreadState() != Thread.State.WAITING) { throw new RuntimeException("Thread " + t.getName() + - " is expected to wait on " + expectedLock + - " but got " + result + - " Thread.State = " + info.getThreadState()); + " is expected to wait on " + expectedLock + + " but got " + result + + " Thread.State = " + info.getThreadState()); } } if ((result != null && !result.equals(expectedLock)) || - (result == null && expectedLock != null)) { + (result == null && expectedLock != null)) { throw new RuntimeException("Thread " + t.getName() + " is blocked on " + - expectedLock + " but got " + result); + expectedLock + " but got " + result); } result = info.getLockOwnerName(); if ((result != null && !result.equals(expectedOwner)) || - (result == null && expectedOwner != null)) { + (result == null && expectedOwner != null)) { throw new RuntimeException("Owner of " + lock + " should be " + - expectedOwner + " but got " + result); + expectedOwner + " but got " + result); } } @@ -100,53 +115,49 @@ } } - static ThreadExecutionSynchronizer thrsync = new ThreadExecutionSynchronizer(); - static ThreadExecutionSynchronizer thrsync1 = new ThreadExecutionSynchronizer(); + private static volatile int dummyCounter = 0; static class LockAThread extends Thread { - public LockAThread() { + private final Phaser p; + public LockAThread(Phaser p) { super("LockAThread"); + this.p = p; } public void run() { synchronized(objA) { - // stop here for LockBThread to hold objB - thrsync.waitForSignal(); - - System.out.println("LockAThread about to block on objB"); - synchronized(objB) {}; + // stop here for LockBThread to hold objB + System.out.println("LockAThread about to block on objB"); + p.arriveAndAwaitAdvance(); // Phase 1 (blocking) + synchronized(objB) { + dummyCounter++; + }; } + p.arriveAndAwaitAdvance(); // Phase 2 (blocking) System.out.println("LockAThread about to exit"); - // The state could be anything. The expected state value - // passed with this method is not verified. - checkBlockedObject(this, null, null, Thread.State.TERMINATED); + // Make sure the current thread is not holding any lock + assertNoLock(this); } } static class LockBThread extends Thread { - public LockBThread() { + private final Phaser p; + public LockBThread(Phaser p) { super("LockBThread"); + this.p = p; } public void run() { synchronized(objB) { - // signal waiting LockAThread. - thrsync.signal(); - - System.out.println("LockBThread about to block on objC"); - // Signal main thread about to block on objC - thrsync1.signal(); - synchronized(objC) {}; + System.out.println("LockBThread about to block on objC"); + p.arriveAndAwaitAdvance(); // Phase 1 (blocking) + // Signal main thread about to block on objC + synchronized(objC) { + dummyCounter++; + }; } + p.arriveAndAwaitAdvance(); // Phase 2 (blocking) System.out.println("LockBThread about to exit"); - // The state could be anything. The expected state value - // passed with this method is not verified. - checkBlockedObject(this, null, null, Thread.State.TERMINATED); - } - - public void aboutToLockC() { - // Stop here till LockBThread about to blocked - // for lock objC. - thrsync1.waitForSignal(); - goSleep(500); + // Make sure the current thread is not holding any lock + assertNoLock(this); } } @@ -154,32 +165,36 @@ private static Object ready = new Object(); private static CheckerThread checker; static class WaitingThread extends Thread { - public WaitingThread() { + private final Phaser p; + public WaitingThread(Phaser p) { super("WaitingThread"); + this.p = p; } public void run() { synchronized(objC) { - System.out.println("WaitingThread about to wait on objC"); - try { - // Signal checker thread, about to wait on objC. - thrsync.signal(); - objC.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - testFailed = true; - } + System.out.println("WaitingThread about to wait on objC"); + try { + // Signal checker thread, about to wait on objC. + p.arriveAndAwaitAdvance(); // Phase 1 (waiting) + objC.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + testFailed = true; + } - // block until CheckerThread finishes checking - System.out.println("WaitingThread about to block on ready"); - // signal checker thread that it is about acquire - // object ready. - thrsync.signal(); - synchronized(ready) {}; + // block until CheckerThread finishes checking + System.out.println("WaitingThread about to block on ready"); + // signal checker thread that it is about acquire + // object ready. + p.arriveAndAwaitAdvance(); // Phase 2 (waiting) + synchronized(ready) { + dummyCounter++; + }; } synchronized(objC) { try { // signal checker thread, about to wait on objC - thrsync.signal(); + p.arriveAndAwaitAdvance(); // Phase 3 (waiting) objC.wait(); } catch (InterruptedException e) { e.printStackTrace(); @@ -190,21 +205,23 @@ } } static class CheckerThread extends Thread { - public CheckerThread() { + private final Phaser p; + public CheckerThread(Phaser p) { super("CheckerThread"); + this.p = p; } private void waitForState(Thread.State state) { - thrsync.waitForSignal(); - while (waiter.getState() != state) { - goSleep(10); + p.arriveAndAwaitAdvance(); + while (!waiter.isInterrupted() && waiter.getState() != state) { + goSleep(10); } } public void run() { synchronized (ready) { // wait until WaitingThread about to wait for objC - waitForState(Thread.State.WAITING); + waitForState(Thread.State.WAITING); // Phase 1 (waiting) checkBlockedObject(waiter, objC, null, Thread.State.WAITING); synchronized (objC) { @@ -213,13 +230,13 @@ // wait for waiter thread to about to enter // synchronized object ready. - waitForState(Thread.State.BLOCKED); + waitForState(Thread.State.BLOCKED); // Phase 2 (waiting) checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED); } // wait for signal from waiting thread that it is about // wait for objC. - waitForState(Thread.State.WAITING); + waitForState(Thread.State.WAITING); // Phase 3 (waiting) synchronized(objC) { checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING); objC.notify(); @@ -235,24 +252,24 @@ LockAThread t1; LockBThread t2; + Phaser p = new Phaser(3); synchronized(objC) { - // The state could be anything. The expected state value - // passed with this method is not verified. - checkBlockedObject(mainThread, null, null, Thread.State.RUNNABLE); + // Make sure the main thread is not holding any lock + assertNoLock(mainThread); // Test deadlock case // t1 holds lockA and attempts to lock B // t2 holds lockB and attempts to lock C - t1 = new LockAThread(); + + t1 = new LockAThread(p); t1.start(); - t2 = new LockBThread(); + t2 = new LockBThread(p); t2.start(); - t2.aboutToLockC(); - + p.arriveAndAwaitAdvance(); // Phase 1 (blocking) + checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED); checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED); - checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED); long[] expectedThreads = new long[3]; expectedThreads[0] = t1.getId(); // blocked on lockB @@ -260,13 +277,14 @@ expectedThreads[2] = mainThread.getId(); // owner of lockC findThreadsBlockedOn(objB, expectedThreads); } - goSleep(100); + p.arriveAndAwaitAdvance(); // Phase 2 (blocking) + p = new Phaser(2); // Test Object.wait() case - waiter = new WaitingThread(); + waiter = new WaitingThread(p); waiter.start(); - checker = new CheckerThread(); + checker = new CheckerThread(p); checker.start(); try { @@ -284,7 +302,7 @@ } private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock) - throws Exception { + throws Exception { ThreadInfo ownerInfo = null; for (int i = 0; i < infos.length; i++) { String blockedLock = infos[i].getLockName(); @@ -292,7 +310,7 @@ long threadId = infos[i].getLockOwnerId(); if (threadId == -1) { throw new RuntimeException("TEST FAILED: " + - lock + " expected to have owner"); + lock + " expected to have owner"); } for (int j = 0; j < infos.length; j++) { if (infos[j].getThreadId() == threadId) { @@ -305,7 +323,7 @@ return ownerInfo; } private static void findThreadsBlockedOn(Object o, long[] expectedThreads) - throws Exception { + throws Exception { String lock = getLockName(o); // Check with ThreadInfo with no stack trace (i.e. no safepoint) ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds()); @@ -317,14 +335,14 @@ } private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads) - throws Exception { + throws Exception { ThreadInfo ownerInfo = null; // Find the thread who is blocking on lock for (int i = 0; i < infos.length; i++) { String blockedLock = infos[i].getLockName(); if (lock.equals(blockedLock)) { System.out.print(infos[i].getThreadName() + - " blocked on " + blockedLock); + " blocked on " + blockedLock); ownerInfo = infos[i]; } } @@ -336,7 +354,7 @@ ownerInfo = findOwnerInfo(infos, lock); threads[count++] = ownerInfo.getThreadId(); System.out.println(" Owner = " + ownerInfo.getThreadName() + - " id = " + ownerInfo.getThreadId()); + " id = " + ownerInfo.getThreadId()); lock = ownerInfo.getLockName(); System.out.print(ownerInfo.getThreadName() + " Id = " + ownerInfo.getThreadId() + @@ -346,13 +364,13 @@ if (count != expectedThreads.length) { throw new RuntimeException("TEST FAILED: " + - "Expected chain of threads not matched; current count =" + count); + "Expected chain of threads not matched; current count =" + count); } for (int i = 0; i < count; i++) { if (threads[i] != expectedThreads[i]) { System.out.println("TEST FAILED: " + - "Unexpected thread in the chain " + threads[i] + - " expected to be " + expectedThreads[i]); + "Unexpected thread in the chain " + threads[i] + + " expected to be " + expectedThreads[i]); } } } diff -r 4dd8f4f14e2c -r 92f9ded70a69 jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java --- a/jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java Wed Jan 29 13:10:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * - * @summary This class is used to synchronize execution of two threads. - * @author Swamy Venkataramanappa - */ - -import java.util.concurrent.Semaphore; - -public class ThreadExecutionSynchronizer { - - private volatile boolean waiting; - private final Semaphore semaphore; - - public ThreadExecutionSynchronizer() { - semaphore = new Semaphore(1); - waiting = false; - } - - // Synchronizes two threads execution points. - // Basically any thread could get scheduled to run and - // it is not possible to know which thread reaches expected - // execution point. So whichever thread reaches a execution - // point first wait for the second thread. When the second thread - // reaches the expected execution point will wake up - // the thread which is waiting here. - void stopOrGo() { - semaphore.acquireUninterruptibly(); // Thread can get blocked. - if (!waiting) { - waiting = true; - // Wait for second thread to enter this method. - while(!semaphore.hasQueuedThreads()) { - try { - Thread.sleep(20); - } catch (InterruptedException xx) {} - } - semaphore.release(); - } else { - waiting = false; - semaphore.release(); - } - } - - // Wrapper function just for code readability. - void waitForSignal() { - stopOrGo(); - goSleep(50); - } - - void signal() { - stopOrGo(); - goSleep(50); - } - - private static void goSleep(long ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - e.printStackTrace(); - System.out.println("Unexpected exception."); - } - } -}