This is an automated email from the ASF dual-hosted git repository. rpopma pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 543863b158464fa7cedb8bb2e10334be6ae1e55d Author: Remko Popma <rem...@yahoo.com> AuthorDate: Tue Apr 26 17:33:48 2022 +0900 LOG4J2-3473 (master) make default WaitStrategy garbage-free add garbage-free version of TimeoutBlockingWaitStrategy and make this the default Async Loggers WaitStrategy --- NOTICE.txt | 3 + .../async/AsyncWaitStrategyFactoryConfigTest.java | 1 - ...egyFactoryIncorrectConfigGlobalLoggersTest.java | 1 - .../async/DefaultAsyncWaitStrategyFactory.java | 1 - .../logging/log4j/core/async/DisruptorUtil.java | 5 - .../core/async/TimeoutBlockingWaitStrategy.java | 135 +++++++++++++++++++++ src/site/asciidoc/manual/garbagefree.adoc | 5 +- 7 files changed, 142 insertions(+), 9 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index a241a12d0e..922a1c7046 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -15,3 +15,6 @@ Copyright 2002-2012 Ramnivas Laddad, Juergen Hoeller, Chris Beams picocli (http://picocli.info) Copyright 2017 Remko Popma + +TimeoutBlockingWaitStrategy.java and parts of Util.java +Copyright 2011 LMAX Ltd. diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java index b27de0c15c..afe08bf49f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.core.async; -import com.lmax.disruptor.TimeoutBlockingWaitStrategy; import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.YieldingWaitStrategy; import org.apache.logging.log4j.core.LoggerContext; diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java index 7adc8b85d5..86b8913daa 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.core.async; -import com.lmax.disruptor.TimeoutBlockingWaitStrategy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.test.categories.AsyncLoggers; import org.apache.logging.log4j.core.LoggerContext; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DefaultAsyncWaitStrategyFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DefaultAsyncWaitStrategyFactory.java index bbe3af696a..a5c54ae4ed 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DefaultAsyncWaitStrategyFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DefaultAsyncWaitStrategyFactory.java @@ -19,7 +19,6 @@ package org.apache.logging.log4j.core.async; import com.lmax.disruptor.BlockingWaitStrategy; import com.lmax.disruptor.BusySpinWaitStrategy; import com.lmax.disruptor.SleepingWaitStrategy; -import com.lmax.disruptor.TimeoutBlockingWaitStrategy; import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.YieldingWaitStrategy; import org.apache.logging.log4j.Logger; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorUtil.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorUtil.java index e37f45473a..200686c672 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorUtil.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/DisruptorUtil.java @@ -22,13 +22,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import com.lmax.disruptor.BlockingWaitStrategy; -import com.lmax.disruptor.BusySpinWaitStrategy; import com.lmax.disruptor.ExceptionHandler; -import com.lmax.disruptor.SleepingWaitStrategy; -import com.lmax.disruptor.TimeoutBlockingWaitStrategy; import com.lmax.disruptor.WaitStrategy; -import com.lmax.disruptor.YieldingWaitStrategy; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.core.util.Integers; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/TimeoutBlockingWaitStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/TimeoutBlockingWaitStrategy.java new file mode 100644 index 0000000000..804e9c901d --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/TimeoutBlockingWaitStrategy.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +// Where the above is the copyright notice of the derivate work, +// below is the copyright notice of the original source of this work: +/* + * Copyright 2011 LMAX Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.core.async; + +import com.lmax.disruptor.AlertException; +import com.lmax.disruptor.Sequence; +import com.lmax.disruptor.SequenceBarrier; +import com.lmax.disruptor.TimeoutException; +import com.lmax.disruptor.WaitStrategy; + +import java.util.concurrent.TimeUnit; + +/** + * Blocking strategy that uses a lock and condition variable for {@link EventProcessor}s waiting on a barrier. + * However, it will periodically wake up if it has been idle for specified period by throwing a + * {@link TimeoutException}. To make use of this, the event handler class should override + * {@link EventHandler#onTimeout(long)}, which the {@link BatchEventProcessor} will call if the timeout occurs. + * + * <p>This strategy can be used when throughput and low-latency are not as important as CPU resource. + */ +// IMPLEMENTATION NOTE: +// This is a copy of the com.lmax.disruptor.TimeoutBlockingWaitStrategy class in disruptor-4.0.0-RC1. +// The difference between this code and the implementation of this class in disruptor-3.4.4 +// is that this class is garbage-free, because it uses a synchronized block instead of a ReentrantLock. +// This class is package-protected, so that it can be used internally as the default WaitStrategy +// by Log4j Async Loggers, but can be removed in a future Log4j release without impacting binary compatibility. +// (disruptor-4.0.0-RC1 requires Java 11 and has other incompatible changes so cannot be used in Log4j 2.x.) +class TimeoutBlockingWaitStrategy implements WaitStrategy { + private final Object mutex = new Object(); + private final long timeoutInNanos; + + /** + * @param timeout how long to wait before waking up + * @param units the unit in which timeout is specified + */ + public TimeoutBlockingWaitStrategy(final long timeout, final TimeUnit units) { + timeoutInNanos = units.toNanos(timeout); + } + + @Override + public long waitFor( + final long sequence, + final Sequence cursorSequence, + final Sequence dependentSequence, + final SequenceBarrier barrier) + throws AlertException, InterruptedException, TimeoutException { + long timeoutNanos = timeoutInNanos; + + long availableSequence; + if (cursorSequence.get() < sequence) { + synchronized (mutex) { + while (cursorSequence.get() < sequence) { + barrier.checkAlert(); + timeoutNanos = awaitNanos(mutex, timeoutNanos); + if (timeoutNanos <= 0) { + throw TimeoutException.INSTANCE; + } + } + } + } + + while ((availableSequence = dependentSequence.get()) < sequence) { + barrier.checkAlert(); + } + + return availableSequence; + } + + @Override + public void signalAllWhenBlocking() { + synchronized (mutex) { + mutex.notifyAll(); + } + } + + @Override + public String toString() { + return "TimeoutBlockingWaitStrategy{" + + "mutex=" + mutex + + ", timeoutInNanos=" + timeoutInNanos + + '}'; + } + + // below code is from com.lmax.disruptor.util.Util class in disruptor 4.0.0-RC1 + private static final int ONE_MILLISECOND_IN_NANOSECONDS = 1_000_000; + + /** + * @param mutex The object to wait on + * @param timeoutNanos The number of nanoseconds to wait for + * @return the number of nanoseconds waited (approximately) + * @throws InterruptedException if the underlying call to wait is interrupted + */ + private static long awaitNanos(final Object mutex, final long timeoutNanos) throws InterruptedException { + long millis = timeoutNanos / ONE_MILLISECOND_IN_NANOSECONDS; + long nanos = timeoutNanos % ONE_MILLISECOND_IN_NANOSECONDS; + + long t0 = System.nanoTime(); + mutex.wait(millis, (int) nanos); + long t1 = System.nanoTime(); + + return timeoutNanos - (t1 - t0); + } +} diff --git a/src/site/asciidoc/manual/garbagefree.adoc b/src/site/asciidoc/manual/garbagefree.adoc index 2dc2b74d81..9ae92a189c 100644 --- a/src/site/asciidoc/manual/garbagefree.adoc +++ b/src/site/asciidoc/manual/garbagefree.adoc @@ -100,9 +100,12 @@ NOTE: As of version 2.6, a Log4j configuration containing a `<Properties>` section will result in temporary objects being created during steady-state logging. -NOTE: The Async Logger Timeout wait strategy (the default) and the Block wait strategy +NOTE: as of version 2.17.3, the default Async Logger wait strategy used by Log4j +(Timeout) is garbage-free. Some of the wait strategies included in LMAX disruptor 3.4.4, +especially `TimeoutBlockingWaitStrategy` and `BlockingWaitStrategy` (Block) are not garbage-free since they cause `java.util.concurrent.locks.AbstractQueuedSynchronizer$Node` objects to be created. +The default wait strategy used by Log4j uses a synchronized block instead of a ReentrantLock to avoid this problem. The Yield and Sleep wait strategies are garbage-free. (For configuring predefined wait strategies, see link:async.html#SysPropsAllAsync[here] and link:async.html#SysPropsMixedSync-Async[here],