You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2022/04/26 09:31:18 UTC
[logging-log4j2] 01/03: LOG4J2-3473 (master) make default WaitStrategy garbage-free
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 <re...@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],