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
This commit is contained in:
Jaroslav Bachorik 2014-01-29 17:37:40 +01:00
parent da17ca59db
commit 9e782fae6d
2 changed files with 111 additions and 178 deletions

View File

@ -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. * 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,18 +27,19 @@
* @summary Basic unit test of ThreadInfo.getLockName() * @summary Basic unit test of ThreadInfo.getLockName()
* and ThreadInfo.getLockOwnerName() * and ThreadInfo.getLockOwnerName()
* @author Mandy Chung * @author Mandy Chung
* @author Jaroslav Bachorik
* *
* @build ThreadExecutionSynchronizer
* @run main/othervm Locks * @run main/othervm Locks
*/ */
import java.lang.management.*; import java.lang.management.*;
import java.util.concurrent.Phaser;
public class Locks { public class Locks {
private static Object objA = new Object(); private static final Object objA = new Object();
private static Object objB = new Object(); private static final Object objB = new Object();
private static Object objC = new Object(); private static final Object objC = new Object();
private static ThreadMXBean tm = ManagementFactory.getThreadMXBean(); private static final ThreadMXBean tm = ManagementFactory.getThreadMXBean();
private static boolean testFailed = false; private static boolean testFailed = false;
@ -46,48 +47,62 @@ public class Locks {
if (lock == null) return null; if (lock == null) return null;
return lock.getClass().getName() + '@' + 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, private static void checkBlockedObject(Thread t, Object lock, Thread owner,
Thread.State expectedState) { Thread.State expectedState) {
ThreadInfo info = tm.getThreadInfo(t.getId()); long tid = t.getId();
ThreadInfo info = tm.getThreadInfo(tid);
String result = info.getLockName(); String result = info.getLockName();
String expectedLock = (lock != null ? getLockName(lock) : null); String expectedLock = (lock != null ? getLockName(lock) : null);
String expectedOwner = (owner != null ? owner.getName() : null); String expectedOwner = (owner != null ? owner.getName() : null);
if (lock != null) { if (lock != null) {
if (expectedState ==Thread.State.BLOCKED) { if (expectedState == Thread.State.BLOCKED) {
int retryCount=0; int retryCount=0;
while(info.getThreadState() != Thread.State.BLOCKED) { while(info.getThreadState() != Thread.State.BLOCKED) {
if (retryCount++ > 500) { if (retryCount++ > 500) {
throw new RuntimeException("Thread " + t.getName() + throw new RuntimeException("Thread " + t.getName() +
" is expected to block on " + expectedLock + " is expected to block on " + expectedLock +
" but got " + result + " but got " + result +
" Thread.State = " + info.getThreadState()); " Thread.State = " + info.getThreadState());
} }
goSleep(100); goSleep(100);
info = tm.getThreadInfo(tid);
result = info.getLockName();
} }
} }
if (expectedState == Thread.State.WAITING && if (expectedState == Thread.State.WAITING &&
info.getThreadState() != Thread.State.WAITING) { info.getThreadState() != Thread.State.WAITING) {
throw new RuntimeException("Thread " + t.getName() + throw new RuntimeException("Thread " + t.getName() +
" is expected to wait on " + expectedLock + " is expected to wait on " + expectedLock +
" but got " + result + " but got " + result +
" Thread.State = " + info.getThreadState()); " Thread.State = " + info.getThreadState());
} }
} }
if ((result != null && !result.equals(expectedLock)) || if ((result != null && !result.equals(expectedLock)) ||
(result == null && expectedLock != null)) { (result == null && expectedLock != null)) {
throw new RuntimeException("Thread " + t.getName() + " is blocked on " + throw new RuntimeException("Thread " + t.getName() + " is blocked on " +
expectedLock + " but got " + result); expectedLock + " but got " + result);
} }
result = info.getLockOwnerName(); result = info.getLockOwnerName();
if ((result != null && !result.equals(expectedOwner)) || if ((result != null && !result.equals(expectedOwner)) ||
(result == null && expectedOwner != null)) { (result == null && expectedOwner != null)) {
throw new RuntimeException("Owner of " + lock + " should be " + throw new RuntimeException("Owner of " + lock + " should be " +
expectedOwner + " but got " + result); expectedOwner + " but got " + result);
} }
} }
@ -100,53 +115,49 @@ public class Locks {
} }
} }
static ThreadExecutionSynchronizer thrsync = new ThreadExecutionSynchronizer(); private static volatile int dummyCounter = 0;
static ThreadExecutionSynchronizer thrsync1 = new ThreadExecutionSynchronizer();
static class LockAThread extends Thread { static class LockAThread extends Thread {
public LockAThread() { private final Phaser p;
public LockAThread(Phaser p) {
super("LockAThread"); super("LockAThread");
this.p = p;
} }
public void run() { public void run() {
synchronized(objA) { synchronized(objA) {
// stop here for LockBThread to hold objB // stop here for LockBThread to hold objB
thrsync.waitForSignal(); System.out.println("LockAThread about to block on objB");
p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
System.out.println("LockAThread about to block on objB"); synchronized(objB) {
synchronized(objB) {}; dummyCounter++;
};
} }
p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
System.out.println("LockAThread about to exit"); System.out.println("LockAThread about to exit");
// The state could be anything. The expected state value // Make sure the current thread is not holding any lock
// passed with this method is not verified. assertNoLock(this);
checkBlockedObject(this, null, null, Thread.State.TERMINATED);
} }
} }
static class LockBThread extends Thread { static class LockBThread extends Thread {
public LockBThread() { private final Phaser p;
public LockBThread(Phaser p) {
super("LockBThread"); super("LockBThread");
this.p = p;
} }
public void run() { public void run() {
synchronized(objB) { synchronized(objB) {
// signal waiting LockAThread. System.out.println("LockBThread about to block on objC");
thrsync.signal(); p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
// Signal main thread about to block on objC
System.out.println("LockBThread about to block on objC"); synchronized(objC) {
// Signal main thread about to block on objC dummyCounter++;
thrsync1.signal(); };
synchronized(objC) {};
} }
p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
System.out.println("LockBThread about to exit"); System.out.println("LockBThread about to exit");
// The state could be anything. The expected state value // Make sure the current thread is not holding any lock
// passed with this method is not verified. assertNoLock(this);
checkBlockedObject(this, null, null, Thread.State.TERMINATED);
}
public void aboutToLockC() {
// Stop here till LockBThread about to blocked
// for lock objC.
thrsync1.waitForSignal();
goSleep(500);
} }
} }
@ -154,32 +165,36 @@ public class Locks {
private static Object ready = new Object(); private static Object ready = new Object();
private static CheckerThread checker; private static CheckerThread checker;
static class WaitingThread extends Thread { static class WaitingThread extends Thread {
public WaitingThread() { private final Phaser p;
public WaitingThread(Phaser p) {
super("WaitingThread"); super("WaitingThread");
this.p = p;
} }
public void run() { public void run() {
synchronized(objC) { synchronized(objC) {
System.out.println("WaitingThread about to wait on objC"); System.out.println("WaitingThread about to wait on objC");
try { try {
// Signal checker thread, about to wait on objC. // Signal checker thread, about to wait on objC.
thrsync.signal(); p.arriveAndAwaitAdvance(); // Phase 1 (waiting)
objC.wait(); objC.wait();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
testFailed = true; testFailed = true;
} }
// block until CheckerThread finishes checking // block until CheckerThread finishes checking
System.out.println("WaitingThread about to block on ready"); System.out.println("WaitingThread about to block on ready");
// signal checker thread that it is about acquire // signal checker thread that it is about acquire
// object ready. // object ready.
thrsync.signal(); p.arriveAndAwaitAdvance(); // Phase 2 (waiting)
synchronized(ready) {}; synchronized(ready) {
dummyCounter++;
};
} }
synchronized(objC) { synchronized(objC) {
try { try {
// signal checker thread, about to wait on objC // signal checker thread, about to wait on objC
thrsync.signal(); p.arriveAndAwaitAdvance(); // Phase 3 (waiting)
objC.wait(); objC.wait();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
@ -190,21 +205,23 @@ public class Locks {
} }
} }
static class CheckerThread extends Thread { static class CheckerThread extends Thread {
public CheckerThread() { private final Phaser p;
public CheckerThread(Phaser p) {
super("CheckerThread"); super("CheckerThread");
this.p = p;
} }
private void waitForState(Thread.State state) { private void waitForState(Thread.State state) {
thrsync.waitForSignal(); p.arriveAndAwaitAdvance();
while (waiter.getState() != state) { while (!waiter.isInterrupted() && waiter.getState() != state) {
goSleep(10); goSleep(10);
} }
} }
public void run() { public void run() {
synchronized (ready) { synchronized (ready) {
// wait until WaitingThread about to wait for objC // 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); checkBlockedObject(waiter, objC, null, Thread.State.WAITING);
synchronized (objC) { synchronized (objC) {
@ -213,13 +230,13 @@ public class Locks {
// wait for waiter thread to about to enter // wait for waiter thread to about to enter
// synchronized object ready. // synchronized object ready.
waitForState(Thread.State.BLOCKED); waitForState(Thread.State.BLOCKED); // Phase 2 (waiting)
checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED); checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED);
} }
// wait for signal from waiting thread that it is about // wait for signal from waiting thread that it is about
// wait for objC. // wait for objC.
waitForState(Thread.State.WAITING); waitForState(Thread.State.WAITING); // Phase 3 (waiting)
synchronized(objC) { synchronized(objC) {
checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING); checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING);
objC.notify(); objC.notify();
@ -235,24 +252,24 @@ public class Locks {
LockAThread t1; LockAThread t1;
LockBThread t2; LockBThread t2;
Phaser p = new Phaser(3);
synchronized(objC) { synchronized(objC) {
// The state could be anything. The expected state value // Make sure the main thread is not holding any lock
// passed with this method is not verified. assertNoLock(mainThread);
checkBlockedObject(mainThread, null, null, Thread.State.RUNNABLE);
// Test deadlock case // Test deadlock case
// t1 holds lockA and attempts to lock B // t1 holds lockA and attempts to lock B
// t2 holds lockB and attempts to lock C // t2 holds lockB and attempts to lock C
t1 = new LockAThread();
t1 = new LockAThread(p);
t1.start(); t1.start();
t2 = new LockBThread(); t2 = new LockBThread(p);
t2.start(); t2.start();
t2.aboutToLockC(); p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED);
checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED); checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED);
checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED);
long[] expectedThreads = new long[3]; long[] expectedThreads = new long[3];
expectedThreads[0] = t1.getId(); // blocked on lockB expectedThreads[0] = t1.getId(); // blocked on lockB
@ -260,13 +277,14 @@ public class Locks {
expectedThreads[2] = mainThread.getId(); // owner of lockC expectedThreads[2] = mainThread.getId(); // owner of lockC
findThreadsBlockedOn(objB, expectedThreads); findThreadsBlockedOn(objB, expectedThreads);
} }
goSleep(100); p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
p = new Phaser(2);
// Test Object.wait() case // Test Object.wait() case
waiter = new WaitingThread(); waiter = new WaitingThread(p);
waiter.start(); waiter.start();
checker = new CheckerThread(); checker = new CheckerThread(p);
checker.start(); checker.start();
try { try {
@ -284,7 +302,7 @@ public class Locks {
} }
private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock) private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
throws Exception { throws Exception {
ThreadInfo ownerInfo = null; ThreadInfo ownerInfo = null;
for (int i = 0; i < infos.length; i++) { for (int i = 0; i < infos.length; i++) {
String blockedLock = infos[i].getLockName(); String blockedLock = infos[i].getLockName();
@ -292,7 +310,7 @@ public class Locks {
long threadId = infos[i].getLockOwnerId(); long threadId = infos[i].getLockOwnerId();
if (threadId == -1) { if (threadId == -1) {
throw new RuntimeException("TEST FAILED: " + throw new RuntimeException("TEST FAILED: " +
lock + " expected to have owner"); lock + " expected to have owner");
} }
for (int j = 0; j < infos.length; j++) { for (int j = 0; j < infos.length; j++) {
if (infos[j].getThreadId() == threadId) { if (infos[j].getThreadId() == threadId) {
@ -305,7 +323,7 @@ public class Locks {
return ownerInfo; return ownerInfo;
} }
private static void findThreadsBlockedOn(Object o, long[] expectedThreads) private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
throws Exception { throws Exception {
String lock = getLockName(o); String lock = getLockName(o);
// Check with ThreadInfo with no stack trace (i.e. no safepoint) // Check with ThreadInfo with no stack trace (i.e. no safepoint)
ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds()); ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds());
@ -317,14 +335,14 @@ public class Locks {
} }
private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads) private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)
throws Exception { throws Exception {
ThreadInfo ownerInfo = null; ThreadInfo ownerInfo = null;
// Find the thread who is blocking on lock // Find the thread who is blocking on lock
for (int i = 0; i < infos.length; i++) { for (int i = 0; i < infos.length; i++) {
String blockedLock = infos[i].getLockName(); String blockedLock = infos[i].getLockName();
if (lock.equals(blockedLock)) { if (lock.equals(blockedLock)) {
System.out.print(infos[i].getThreadName() + System.out.print(infos[i].getThreadName() +
" blocked on " + blockedLock); " blocked on " + blockedLock);
ownerInfo = infos[i]; ownerInfo = infos[i];
} }
} }
@ -336,7 +354,7 @@ public class Locks {
ownerInfo = findOwnerInfo(infos, lock); ownerInfo = findOwnerInfo(infos, lock);
threads[count++] = ownerInfo.getThreadId(); threads[count++] = ownerInfo.getThreadId();
System.out.println(" Owner = " + ownerInfo.getThreadName() + System.out.println(" Owner = " + ownerInfo.getThreadName() +
" id = " + ownerInfo.getThreadId()); " id = " + ownerInfo.getThreadId());
lock = ownerInfo.getLockName(); lock = ownerInfo.getLockName();
System.out.print(ownerInfo.getThreadName() + " Id = " + System.out.print(ownerInfo.getThreadName() + " Id = " +
ownerInfo.getThreadId() + ownerInfo.getThreadId() +
@ -346,13 +364,13 @@ public class Locks {
if (count != expectedThreads.length) { if (count != expectedThreads.length) {
throw new RuntimeException("TEST FAILED: " + 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++) { for (int i = 0; i < count; i++) {
if (threads[i] != expectedThreads[i]) { if (threads[i] != expectedThreads[i]) {
System.out.println("TEST FAILED: " + System.out.println("TEST FAILED: " +
"Unexpected thread in the chain " + threads[i] + "Unexpected thread in the chain " + threads[i] +
" expected to be " + expectedThreads[i]); " expected to be " + expectedThreads[i]);
} }
} }
} }

View File

@ -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.");
}
}
}