Skip to content

Commit

Permalink
fix SLF4J-469
Browse files Browse the repository at this point in the history
  • Loading branch information
ceki committed Dec 16, 2019
1 parent ca93154 commit c6c7a98
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 10 deletions.
26 changes: 21 additions & 5 deletions integration/build.xml
Expand Up @@ -56,7 +56,12 @@
<pathelement location="../slf4j-simple/target/slf4j-simple-${currentVersion}.jar" />
</path >

<path id="pathNoBinding">
<pathelement location="target/test-classes/" />
<pathelement location="../slf4j-api/target/slf4j-api-${currentVersion}.jar" />
</path >


<path id="incompatibleMultiBinding">
<pathelement location="target/test-classes/" />
<pathelement location="../slf4j-api/target/slf4j-api-${currentVersion}.jar" />
Expand Down Expand Up @@ -102,11 +107,12 @@
</target>

<target name="testAll" depends="init,
testMissingSingletonMethod,
testMissingSingletonMethod,
testMismatch,
testMatch,
testMultiBinding,
testIncompatibleMultiBinding,
testNoBinding,
testMatch,
testMultiBinding,
testIncompatibleMultiBinding,
testFuture_16Series,
testActiveSecurityManager">
</target>
Expand Down Expand Up @@ -215,7 +221,17 @@
</junit>

</target>


<target name="testNoBinding">
<junit printsummary="yes" fork="no" haltonfailure="yes">
<classpath refid="pathNoBinding" />
<formatter type="plain" />
<test fork="yes" todir="target/unit-reports"
outfile="TEST-NOBINDING"
name="org.slf4j.helpers.NoBindingMultithreadedInitializationTest2" />
</junit>
</target>


<condition property="runFromWithinIntegrationModule">
<contains string="${user.dir}" substring="integration" />
Expand Down
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2004-2016 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package org.slf4j.helpers;

import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerAccessingThread2 extends Thread {
private static int LOOP_LEN = 64;

final CyclicBarrier barrier;
final int count;
final AtomicLong eventCount;
List<Logger> loggerList;

public LoggerAccessingThread2(final CyclicBarrier barrier, List<Logger> loggerList, final int count, final AtomicLong eventCount) {
this.barrier = barrier;
this.loggerList = loggerList;
this.count = count;
this.eventCount = eventCount;
}

public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}

String loggerNamePrefix = this.getClass().getName();
for (int i = 0; i < LOOP_LEN; i++) {
Logger logger = LoggerFactory.getLogger(loggerNamePrefix + "-" + count + "-" + i);
loggerList.add(logger);
Thread.yield();
logger.info("in run method");
eventCount.getAndIncrement();
}
}
}
@@ -0,0 +1,112 @@
/**
* Copyright (c) 2004-2016 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package org.slf4j.helpers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.EventRecodingLogger;

import junit.framework.TestCase;

public class NoBindingMultithreadedInitializationTest2 extends TestCase {
final protected static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;

private final List<Logger> createdLoggers = Collections.synchronizedList(new ArrayList<Logger>());

protected final AtomicLong eventCount = new AtomicLong(0);
final private CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);

int diff = new Random().nextInt(10000);


public NoBindingMultithreadedInitializationTest2(String name) {
super(name);
}

public void testNoBindingMultiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
@SuppressWarnings("unused")
LoggerAccessingThread2[] accessors = harness();

Logger logger = LoggerFactory.getLogger(getClass().getName());
logger.info("hello");
eventCount.getAndIncrement();

assertAllSubstLoggersAreFixed();
long recordedEventCount = getRecordedEventCount();
int LENIENCY_COUNT = 16;

long expectedEventCount = eventCount.get() + extraLogEvents();

assertTrue(expectedEventCount + " >= " + recordedEventCount, expectedEventCount >= recordedEventCount);
assertTrue(expectedEventCount + " < " + recordedEventCount + "+" + LENIENCY_COUNT,
expectedEventCount < recordedEventCount + LENIENCY_COUNT);
}

protected int extraLogEvents() {
return 0;
}

private void assertAllSubstLoggersAreFixed() {
for (Logger logger : createdLoggers) {
if (logger instanceof SubstituteLogger) {
SubstituteLogger substLogger = (SubstituteLogger) logger;
if (substLogger.delegate() instanceof EventRecodingLogger)
fail("substLogger " + substLogger.getName() + " has a delegate of type EventRecodingLogger");
}
}
}

private LoggerAccessingThread2[] harness() throws InterruptedException, BrokenBarrierException {
LoggerAccessingThread2[] threads = new LoggerAccessingThread2[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new LoggerAccessingThread2(barrier, createdLoggers, i, eventCount);
threads[i].start();
}

// trigger barrier
barrier.await();

for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}

return threads;
}

final String loggerName = this.getClass().getName();

protected long getRecordedEventCount() {
return eventCount.get();
}

}
13 changes: 9 additions & 4 deletions slf4j-api/src/main/java/org/slf4j/LoggerFactory.java
Expand Up @@ -150,10 +150,6 @@ private final static void bind() {
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
Expand All @@ -177,9 +173,18 @@ private final static void bind() {
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
} finally {
postBindCleanUp();
}
}

private static void postBindCleanUp() {
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
}

private static void fixSubstituteLoggers() {
synchronized (SUBST_FACTORY) {
SUBST_FACTORY.postInitialization();
Expand Down
Expand Up @@ -22,7 +22,7 @@ abstract public class MultithreadedInitializationTest {

private final List<Logger> createdLoggers = Collections.synchronizedList(new ArrayList<Logger>());

final private AtomicLong eventCount = new AtomicLong(0);
protected final AtomicLong eventCount = new AtomicLong(0);
final private CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);

int diff = new Random().nextInt(10000);
Expand Down

0 comments on commit c6c7a98

Please sign in to comment.