--- old/src/java.logging/share/classes/java/util/logging/LogManager.java 2015-04-24 19:02:54.000000000 +0200 +++ new/src/java.logging/share/classes/java/util/logging/LogManager.java 2015-04-24 19:02:53.000000000 +0200 @@ -33,6 +33,7 @@ import java.lang.ref.WeakReference; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; import sun.misc.JavaAWTAccess; import sun.misc.SharedSecrets; @@ -180,9 +181,12 @@ private volatile boolean readPrimordialConfiguration; // Have we initialized global (root) handlers yet? // This gets set to false in readConfiguration - private boolean initializedGlobalHandlers = true; + private volatile boolean initializeGlobalHandlersCalled = true; + private volatile boolean initializeGlobalHandlersDone = true; // True if JVM death is imminent and the exit hook has been called. - private boolean deathImminent; + private volatile boolean deathImminent; + // A concurrency lock for reset() and readConfiguration(). + private final ReentrantLock configurationLock = new ReentrantLock(); // This list contains the loggers for which some handlers have been // explicitly configured in the configuration file. @@ -263,13 +267,14 @@ // before synchronized block. Otherwise deadlocks are possible. LogManager mgr = manager; + // Note that death is imminent. + deathImminent = true; + // If the global handlers haven't been initialized yet, we // don't want to initialize them just so we can close them! - synchronized (LogManager.this) { - // Note that death is imminent. - deathImminent = true; - initializedGlobalHandlers = true; - } + // So we're going to pretend that initializeGlobalHandlers has + // been called. + initializeGlobalHandlersCalled = true; // Do a reset to close all active handlers. reset(); @@ -954,7 +959,7 @@ try { Class clz = ClassLoader.getSystemClassLoader().loadClass(type); Handler hdl = (Handler) clz.newInstance(); - // Check if there is a property defining the + // Check if there is a property defining the // this handler's level. String levs = getProperty(type + ".level"); if (levs != null) { @@ -1314,7 +1319,12 @@ public void reset() throws SecurityException { checkPermission(); List persistent; - synchronized (this) { + + // We don't want reset() and readConfiguration() + // to run in parallel + configurationLock.lock(); + try { + props = new Properties(); // make sure we keep the loggers persistent until reset is done. // Those are the loggers for which we previously created a @@ -1324,24 +1334,30 @@ closeOnResetLoggers.clear(); // Since we are doing a reset we no longer want to initialize // the global handlers, if they haven't been initialized yet. - initializedGlobalHandlers = true; + initializeGlobalHandlersCalled = true; + + for (LoggerContext cx : contexts()) { + resetLoggerContext(cx); + } + + persistent.clear(); + } finally { + configurationLock.unlock(); } - for (LoggerContext cx : contexts()) { - Enumeration enum_ = cx.getLoggerNames(); - while (enum_.hasMoreElements()) { - String name = enum_.nextElement(); - Logger logger = cx.findLogger(name); - if (logger != null) { - resetLogger(logger); - } + } + + private void resetLoggerContext(LoggerContext cx) { + Enumeration enum_ = cx.getLoggerNames(); + while (enum_.hasMoreElements()) { + String name = enum_.nextElement(); + Logger logger = cx.findLogger(name); + if (logger != null) { + resetLogger(logger); } } - persistent.clear(); } - // Private method to reset an individual target logger. - private void resetLogger(Logger logger) { - // Close all the Logger's handlers. + private void closeHandlers(Logger logger) { Handler[] targets = logger.getHandlers(); for (Handler h : targets) { logger.removeHandler(h); @@ -1351,6 +1367,14 @@ // Problems closing a handler? Keep going... } } + } + + // Private method to reset an individual target logger. + private void resetLogger(Logger logger) { + // Close all the Logger handlers. + closeHandlers(logger); + + // Reset Logger level String name = logger.getName(); if (name != null && name.equals("")) { // This is the root logger. @@ -1407,48 +1431,59 @@ */ public void readConfiguration(InputStream ins) throws IOException, SecurityException { checkPermission(); - reset(); - // Load the properties - try { - props.load(ins); - } catch (IllegalArgumentException x) { - // props.load may throw an IllegalArgumentException if the stream - // contains malformed Unicode escape sequences. - // We wrap that in an IOException as readConfiguration is - // specified to throw IOException if there are problems reading - // from the stream. - // Note: new IOException(x.getMessage(), x) allow us to get a more - // concise error message than new IOException(x); - throw new IOException(x.getMessage(), x); - } + // We don't want reset() and readConfiguration() to run + // in parallel. + configurationLock.lock(); - // Instantiate new configuration objects. - String names[] = parseClassNames("config"); + boolean propertiesReloaded = false; + try { + reset(); - for (String word : names) { + // Load the properties try { - Class clz = ClassLoader.getSystemClassLoader().loadClass(word); - clz.newInstance(); - } catch (Exception ex) { - System.err.println("Can't load config class \"" + word + "\""); - System.err.println("" + ex); - // ex.printStackTrace(); + props.load(ins); + propertiesReloaded = true; + } catch (IllegalArgumentException x) { + // props.load may throw an IllegalArgumentException if the stream + // contains malformed Unicode escape sequences. + // We wrap that in an IOException as readConfiguration is + // specified to throw IOException if there are problems reading + // from the stream. + // Note: new IOException(x.getMessage(), x) allow us to get a more + // concise error message than new IOException(x); + throw new IOException(x.getMessage(), x); } - } - // Set levels on any pre-existing loggers, based on the new properties. - setLevelsOnExistingLoggers(); + // Instantiate new configuration objects. + String names[] = parseClassNames("config"); + + for (String word : names) { + try { + Class clz = ClassLoader.getSystemClassLoader().loadClass(word); + clz.newInstance(); + } catch (Exception ex) { + System.err.println("Can't load config class \"" + word + "\""); + System.err.println("" + ex); + // ex.printStackTrace(); + } + } + + // Set levels on any pre-existing loggers, based on the new properties. + setLevelsOnExistingLoggers(); - try { - invokeConfigurationListeners(); } finally { // Note that we need to reinitialize global handles when // they are first referenced. - synchronized (this) { - initializedGlobalHandlers = false; + // Note: Marking global handlers as not initialized must be done + // before unlocking. + if (propertiesReloaded) { + initializeGlobalHandlersCalled = initializeGlobalHandlersDone = false; } + configurationLock.unlock(); } + + invokeConfigurationListeners(); } /** @@ -1575,20 +1610,40 @@ // Private method to load the global handlers. // We do the real work lazily, when the global handlers // are first used. - private synchronized void initializeGlobalHandlers() { - if (initializedGlobalHandlers) { + private void initializeGlobalHandlers() { + if (initializeGlobalHandlersCalled && initializeGlobalHandlersDone) { + // Nothing to do: return. return; } - initializedGlobalHandlers = true; - - if (deathImminent) { - // Aaargh... - // The VM is shutting down and our exit hook has been called. - // Avoid allocating global handlers. - return; + // If we have not initialized global handlers yet (or need to + // reinitialize them), lets do it now (this case is indicated by + // initializeGlobalHandlersCalled == false). + // If we are in the process of initializing global handlers we + // also need to lock & wait (this case is indicated by + // initializeGlobalHandlersDone == false). + // So if either initializeGlobalHandlersCalled or + // initializeGlobalHandlersDone are false we need to acquire the lock. + configurationLock.lock(); + try { + if (initializeGlobalHandlersCalled) return; // recursive call + // set initializeGlobalHandlersCalled first to avoid getting an + // infinite recursion when loadLoggerHandlers(...) is going to + // call addHandler(...) + initializeGlobalHandlersCalled = true; + initializeGlobalHandlersDone = false; + + if (deathImminent) { + // Aaargh... + // The VM is shutting down and our exit hook has been called. + // Avoid allocating global handlers. + return; + } + loadLoggerHandlers(rootLogger, null, "handlers"); + } finally { + initializeGlobalHandlersDone = true; + configurationLock.unlock(); } - loadLoggerHandlers(rootLogger, null, "handlers"); } static final Permission controlPermission = new LoggingPermission("control", null); @@ -1683,7 +1738,7 @@ // Private method to be called when the configuration has // changed to apply any level settings to any pre-existing loggers. - synchronized private void setLevelsOnExistingLoggers() { + private void setLevelsOnExistingLoggers() { Enumeration enum_ = props.propertyNames(); while (enum_.hasMoreElements()) { String key = (String)enum_.nextElement();