Mercurial > hg > release > icedtea7-forest-2.1 > jdk
changeset 4967:871acb7cd95c
8010939: Deadlock in LogManager
Summary: re-order locks to avoid deadlock
Reviewed-by: mchung, alanb
author | jgish |
---|---|
date | Fri, 19 Apr 2013 16:50:10 -0700 |
parents | 776ac4b51f15 |
children | 2d3faf217561 |
files | src/share/classes/java/util/logging/LogManager.java test/java/util/logging/DrainFindDeadlockTest.java |
diffstat | 2 files changed, 202 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/java/util/logging/LogManager.java Tue May 14 08:07:08 2013 -0700 +++ b/src/share/classes/java/util/logging/LogManager.java Fri Apr 19 16:50:10 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -33,10 +33,8 @@ import java.lang.ref.WeakReference; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.net.URL; import sun.misc.JavaAWTAccess; import sun.misc.SharedSecrets; -import sun.security.action.GetPropertyAction; /** * There is a single global LogManager object that is used to @@ -151,7 +149,6 @@ // The global LogManager object private static LogManager manager; - private final static Handler[] emptyHandlers = { }; private Properties props = new Properties(); private PropertyChangeSupport changes = new PropertyChangeSupport(LogManager.class); @@ -516,14 +513,11 @@ throw new NullPointerException(); } - // cleanup some Loggers that have been GC'ed - manager.drainLoggerRefQueueBounded(); - LoggerWeakRef ref = namedLoggers.get(name); if (ref != null) { if (ref.get() == null) { - // It's possible that the Logger was GC'ed after the - // drainLoggerRefQueueBounded() call above so allow + // It's possible that the Logger was GC'ed after a + // drainLoggerRefQueueBounded() call so allow // a new one to be registered. removeLogger(name); } else { @@ -571,6 +565,8 @@ return true; } + // note: all calls to removeLogger are synchronized on LogManager's + // intrinsic lock void removeLogger(String name) { namedLoggers.remove(name); } @@ -853,6 +849,7 @@ if (name == null) { throw new NullPointerException(); } + drainLoggerRefQueueBounded(); LoggerContext cx = getUserContext(); if (cx.addLocalLogger(logger)) { // Do we have a per logger handler too?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/logging/DrainFindDeadlockTest.java Fri Apr 19 16:50:10 2013 -0700 @@ -0,0 +1,196 @@ +/* + * 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) { + }; + } + } + } +}