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 08:08:42 UTC
[logging-log4j2] 01/03: LOG4J2-3473 add garbage-free version of TimeoutBlockingWaitStrategy and make this the default Async Loggers WaitStrategy
This is an automated email from the ASF dual-hosted git repository.
rpopma pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 06b75ef230dae1b037c0cf26dfcbb53322982517
Author: Remko Popma <re...@yahoo.com>
AuthorDate: Fri Apr 15 12:23:50 2022 +0900
LOG4J2-3473 add garbage-free version of TimeoutBlockingWaitStrategy and make this the default Async Loggers WaitStrategy
---
NOTICE.txt | 3 +
.../logging/log4j/core/async/DisruptorUtil.java | 1 -
.../core/async/TimeoutBlockingWaitStrategy.java | 135 +++++++++++++++++++++
3 files changed, 138 insertions(+), 1 deletion(-)
diff --git a/NOTICE.txt b/NOTICE.txt
index a18190b0c7..243a0391fb 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/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 5e292398a8..4924202de0 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
@@ -25,7 +25,6 @@ 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;
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);
+ }
+}