/*
* Copyright (c) 2013, 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.
*/
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.Thread.State;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.Map;
/**
* @test
* @bug 8010939
* @summary check for deadlock between findLogger() and drainLoggerRefQueueBounded()
* @author jim.gish@oracle.com
* @build DrainFindDeadlockTest
* @run main/othervm/timeout=10 DrainFindDeadlockTest
*/
/**
* This test is checking for a deadlock between
* LogManager$LoggerContext.findLogger() and
* LogManager.drainLoggerRefQueueBounded() (which could happen by calling
* Logger.getLogger() and LogManager.readConfiguration() in different threads)
*/
public class DrainFindDeadlockTest {
private LogManager mgr = LogManager.getLogManager();
private final static int MAX_ITERATIONS = 100;
// Get a ThreadMXBean so we can check for deadlock. N.B. this may
// not be supported on all platforms, which means we will have to
// resort to the traditional test timeout method. However, if
// we have the support we'll get the deadlock details if one
// is detected.
private final static ThreadMXBean threadMXBean =
ManagementFactory.getThreadMXBean();
private final boolean threadMXBeanDeadlockSupported =
threadMXBean.isSynchronizerUsageSupported();
public static void main(String... args) throws IOException, Exception {
new DrainFindDeadlockTest().testForDeadlock();
}
public static void randomDelay() {
int runs = (int) Math.random() * 1000000;
int c = 0;
for (int i=0; i<runs; ++i) {
c=c+i;
}
}
public void testForDeadlock() throws IOException, Exception {
System.out.println("Deadlock detection "
+ (threadMXBeanDeadlockSupported ? "is" : "is not") +
" available.");
Thread setup = new Thread(new SetupLogger(), "SetupLogger");
Thread readConfig = new Thread(new ReadConfig(), "ReadConfig");
Thread check = new Thread(new DeadlockChecker(setup, readConfig),
"DeadlockChecker");
// make the threads daemon threads so they will go away when the
// test exits
setup.setDaemon(true);
readConfig.setDaemon(true);
check.setDaemon(true);
check.start(); setup.start(); readConfig.start();
try {
check.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
try {
readConfig.join();
setup.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Test passed");
}
class SetupLogger implements Runnable {
Logger logger = null;
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
for (int i=0; i < MAX_ITERATIONS; i++) {
logger = Logger.getLogger("DrainFindDeadlockTest"+i);
DrainFindDeadlockTest.randomDelay();
}
}
}
class ReadConfig implements Runnable {
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
for (int i=0; i < MAX_ITERATIONS; i++) {
try {
mgr.readConfiguration();
} catch (IOException | SecurityException ex) {
throw new RuntimeException("FAILED: test setup problem", ex);
}
DrainFindDeadlockTest.randomDelay();
}
}
}
class DeadlockChecker implements Runnable {
Thread t1, t2;
DeadlockChecker(Thread t1, Thread t2) {
this.t1 = t1;
this.t2 = t2;
}
void checkState(Thread x, Thread y) {
// System.out.println("checkstate");
boolean isXblocked = x.getState().equals(State.BLOCKED);
boolean isYblocked = y.getState().equals(State.BLOCKED);
long[] deadlockedThreads = null;
if (isXblocked && isYblocked) {
System.out.println("threads blocked");
// they are both blocked, but this doesn't necessarily mean
// they are deadlocked
if (threadMXBeanDeadlockSupported) {
System.out.println("checking for deadlock");
deadlockedThreads = threadMXBean.findDeadlockedThreads();
} else {
System.out.println("Can't check for deadlock");
}
if (deadlockedThreads != null) {
System.out.println("We detected a deadlock! ");
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(
deadlockedThreads, true, true);
for (ThreadInfo threadInfo: threadInfos) {
System.out.println(threadInfo);
}
throw new RuntimeException("TEST FAILED: Deadlock detected");
}
System.out.println("We may have a deadlock");
Map<Thread, StackTraceElement[]> threadMap =
Thread.getAllStackTraces();
dumpStack(threadMap.get(x), x);
dumpStack(threadMap.get(y), y);
}
}
private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) {
if (aStackElt != null) {
System.out.println("Thread:" + aThread.getName() + ": " +
aThread.getState());
for (StackTraceElement element: aStackElt) {
System.out.println(" " + element);
}
}
}
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
for (int i=0; i < MAX_ITERATIONS*2; i++) {
checkState(t1, t2);
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
};
}
}
}
}