--- old/test/java/util/logging/DrainFindDeadlockTest.java 2013-06-17 15:15:12.000000000 +0200 +++ new/test/java/util/logging/DrainFindDeadlockTest.java 2013-06-17 15:15:12.000000000 +0200 @@ -58,6 +58,9 @@ private final boolean threadMXBeanDeadlockSupported = threadMXBean.isSynchronizerUsageSupported(); + private volatile boolean terminate = false; + private volatile Exception failed = null; + public static void main(String... args) throws IOException, Exception { new DrainFindDeadlockTest().testForDeadlock(); } @@ -71,6 +74,15 @@ } } + public void fail(Exception failure) { + if (failed == null) { + failed = failure; + } else { + failure.printStackTrace(); + } + terminate = true; + } + public void testForDeadlock() throws IOException, Exception { System.out.println("Deadlock detection " + (threadMXBeanDeadlockSupported ? "is" : "is not") + @@ -92,13 +104,20 @@ } catch (InterruptedException ex) { ex.printStackTrace(); } - try { - readConfig.join(); - setup.join(); - } catch (InterruptedException ex) { - ex.printStackTrace(); + + if (failed == null) { + terminate = true; + try { + readConfig.join(); + setup.join(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + System.out.println("Test passed"); + } else { + System.err.println("Test Failed: " + failed); + throw failed; } - System.out.println("Test passed"); } class SetupLogger implements Runnable { @@ -107,9 +126,9 @@ @Override public void run() { System.out.println("Running " + Thread.currentThread().getName()); - - for (int i=0; i < MAX_ITERATIONS; i++) { - logger = Logger.getLogger("DrainFindDeadlockTest"+i); + int i = 0; + while (!terminate) { + logger = Logger.getLogger("DrainFindDeadlockTest"+(i++)); DrainFindDeadlockTest.randomDelay(); } } @@ -119,11 +138,11 @@ @Override public void run() { System.out.println("Running " + Thread.currentThread().getName()); - for (int i=0; i < MAX_ITERATIONS; i++) { + while (!terminate) { try { mgr.readConfiguration(); } catch (IOException | SecurityException ex) { - throw new RuntimeException("FAILED: test setup problem", ex); + fail(new RuntimeException("FAILED: test setup problem", ex)); } DrainFindDeadlockTest.randomDelay(); } @@ -131,14 +150,58 @@ } class DeadlockChecker implements Runnable { - Thread t1, t2; - + final Thread x, y; + final long[] threadIds; DeadlockChecker(Thread t1, Thread t2) { - this.t1 = t1; - this.t2 = t2; + this.x = t1; + this.y = t2; + threadIds = new long[] { t1.getId(), t2.getId() }; + } + + boolean threadsBlocked(ThreadInfo[] xyThreadInfos) { + for (ThreadInfo threadInfo : xyThreadInfos) { + switch (threadInfo.getThreadState()) { + case BLOCKED: + case WAITING: + case TIMED_WAITING: + continue; + default: + System.out.println("Thread " + threadInfo.getThreadName() + + " is not blocked: " + threadInfo.getThreadState()); + return false; + } + } + return xyThreadInfos.length > 0; } - void checkState(Thread x, Thread y) { + long[] findDeadlockedThreads() { + // It seems that calling ThreadMXBean.findDeadlockedThreads() + // is not always reliable. Until that is fixed we will only + // call it if we find that our two threads are simultaneously + // BLOCKED or WAITING. + System.out.println("checking thread infos"); + ThreadInfo[] xyThreadInfos = threadMXBean.getThreadInfo(threadIds, true, true); + if (!threadsBlocked(xyThreadInfos)) return null; + /* + if (xyThreadInfo != null && xyThreadInfos.length == 2) { + if (xyThreadInfo[0].getLockOwnerId() != xyThreadInfo[1].getThreadId()) { + return null; + } + if (xyThreadInfo[1].getLockOwnerId() != xyThreadInfo[0].getThreadId()) { + return null; + } + } + */ + System.out.println("checking for deadlock"); + long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); + if (deadlockedThreads != null) { + xyThreadInfos = threadMXBean.getThreadInfo(deadlockedThreads, true, true); + if (!threadsBlocked(xyThreadInfos)) return null; + } + return deadlockedThreads; + } + + void checkState() { // System.out.println("checkstate"); boolean isXblocked = x.getState().equals(State.BLOCKED); boolean isYblocked = y.getState().equals(State.BLOCKED); @@ -149,8 +212,7 @@ // they are both blocked, but this doesn't necessarily mean // they are deadlocked if (threadMXBeanDeadlockSupported) { - System.out.println("checking for deadlock"); - deadlockedThreads = threadMXBean.findDeadlockedThreads(); + deadlockedThreads = findDeadlockedThreads(); } else { System.out.println("Can't check for deadlock"); } @@ -159,15 +221,20 @@ ThreadInfo[] threadInfos = threadMXBean.getThreadInfo( deadlockedThreads, true, true); for (ThreadInfo threadInfo: threadInfos) { + System.out.println("Thread " + threadInfo.getThreadName() + " blocked on " + + threadInfo.getLockName() + " owned by " + threadInfo.getLockOwnerName()); + } + for (ThreadInfo threadInfo: threadInfos) { System.out.println(threadInfo); } - throw new RuntimeException("TEST FAILED: Deadlock detected"); - } - System.out.println("We may have a deadlock"); - Map threadMap = + fail(new RuntimeException("TEST FAILED: Deadlock detected")); + } else if (!threadMXBeanDeadlockSupported) { + System.out.println("We may have a deadlock"); + Map threadMap = Thread.getAllStackTraces(); - dumpStack(threadMap.get(x), x); - dumpStack(threadMap.get(y), y); + dumpStack(threadMap.get(x), x); + dumpStack(threadMap.get(y), y); + } } } @@ -185,7 +252,8 @@ public void run() { System.out.println("Running " + Thread.currentThread().getName()); for (int i=0; i < MAX_ITERATIONS*2; i++) { - checkState(t1, t2); + if (terminate) return; + checkState(); try { Thread.sleep(10); } catch (InterruptedException ex) {