# HG changeset patch # User jgish # Date 1366415410 25200 # Node ID 871acb7cd95cb1dedae326e0e8eb4630f4eb980d # Parent 776ac4b51f1538de78054f7774b9bbefc91374cb 8010939: Deadlock in LogManager Summary: re-order locks to avoid deadlock Reviewed-by: mchung, alanb diff -r 776ac4b51f15 -r 871acb7cd95c src/share/classes/java/util/logging/LogManager.java --- 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? diff -r 776ac4b51f15 -r 871acb7cd95c test/java/util/logging/DrainFindDeadlockTest.java --- /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 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) { + }; + } + } + } +}