From 299669ade22c105de8068087665f0953610ed681 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sun, 9 Jun 2013 13:25:46 +0100 Subject: [PATCH] Bug 176 - Initialization (getILoggerFactory) is not thread safe Detect deadlocks with a configurable timeout that defaults to 10 seconds. If the timeout occurs an exception will be thrown. --- .../src/main/java/org/slf4j/LoggerFactory.java | 58 +++++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java index 3994188..f85ae46 100644 --- a/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java +++ b/slf4j-api/src/main/java/org/slf4j/LoggerFactory.java @@ -27,6 +27,10 @@ package org.slf4j; import java.io.IOException; import java.net.URL; import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.slf4j.helpers.NOPLoggerFactory; import org.slf4j.helpers.SubstituteLoggerFactory; @@ -64,15 +68,20 @@ public final class LoggerFactory { static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also " + UNSUCCESSFUL_INIT_URL; + static final String CONCURRENT_INIT_URL = CODES_PREFIX + "#concurrentInitFailure"; + static final String CONCURRENT_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also " + + CONCURRENT_INIT_URL; + static final int UNINITIALIZED = 0; - static final int ONGOING_INITIALIZATION = 1; - static final int FAILED_INITIALIZATION = 2; - static final int SUCCESSFUL_INITIALIZATION = 3; - static final int NOP_FALLBACK_INITIALIZATION = 4; + static final int FAILED_INITIALIZATION = 1; + static final int SUCCESSFUL_INITIALIZATION = 2; + static final int NOP_FALLBACK_INITIALIZATION = 3; - static int INITIALIZATION_STATE = UNINITIALIZED; + static volatile int INITIALIZATION_STATE = UNINITIALIZED; static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory(); static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory(); + static final ReentrantLock INIT_LOCK = new ReentrantLock(true); + static final int INIT_TIMEOUT_MS = Integer.valueOf(System.getProperty("org.slf4j.LoggerFactory.initialisationTimeout", "10000")); /** * It is LoggerFactory's responsibility to track version changes and manage @@ -150,6 +159,9 @@ public final class LoggerFactory { Util.report("Upgrade your binding to version 1.6.x."); } throw nsme; + } catch (ExceptionInInitializerError e) { + failedBinding(e); + throw new IllegalStateException("Unexpected initialization failure", e); } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); @@ -290,10 +302,6 @@ public final class LoggerFactory { * @return the ILoggerFactory instance in use */ public static ILoggerFactory getILoggerFactory() { - if (INITIALIZATION_STATE == UNINITIALIZED) { - INITIALIZATION_STATE = ONGOING_INITIALIZATION; - performInitialization(); - } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); @@ -301,10 +309,34 @@ public final class LoggerFactory { return NOP_FALLBACK_FACTORY; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); - case ONGOING_INITIALIZATION: - // support re-entrant behavior. - // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 - return TEMP_FACTORY; + case UNINITIALIZED: + if (INIT_LOCK.isHeldByCurrentThread()) { + // support re-entrant behavior. + // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 + return TEMP_FACTORY; + } + + try { + if (!INIT_LOCK.tryLock(INIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + // Initialisation is taking too long + throw new IllegalStateException(CONCURRENT_INIT_MSG); + } + } catch (InterruptedException e) { + // Don't hide interrupts (they may be caught by non-logging code) + Thread.currentThread().interrupt(); + + // Assume that the timeout expired + throw new IllegalStateException(CONCURRENT_INIT_MSG); + } + + try { + if (INITIALIZATION_STATE == UNINITIALIZED) + performInitialization(); + + return getILoggerFactory(); + } finally { + INIT_LOCK.unlock(); + } } throw new IllegalStateException("Unreachable code"); } -- 1.8.0.2