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],