This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 480ec3572b fix: make HierarchicalWheelTimer threads daemon and bound
its task queue (#6668) (#7518)
480ec3572b is described below
commit 480ec3572b59bce17c747a20aedb3c4ccdfc88bc
Author: wy471x <[email protected]>
AuthorDate: Sat Oct 10 09:24:23 2026 +0800
fix: make HierarchicalWheelTimer threads daemon and bound its task queue
(#6668) (#7518)
---
.../common/timer/HierarchicalWheelTimer.java | 21 ++++++++---
.../common/timer/HierarchicalWheelTimerTest.java | 42 ++++++++++++++++++++++
2 files changed, 58 insertions(+), 5 deletions(-)
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java
index 1abffc5095..977aa762a8 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java
@@ -18,11 +18,16 @@
package org.apache.shenyu.common.timer;
import java.util.Objects;
+import org.apache.shenyu.common.concurrent.MemorySafeTaskQueue;
import org.apache.shenyu.common.concurrent.ShenyuThreadFactory;
+import org.apache.shenyu.common.concurrent.ShenyuThreadPoolExecutor;
+import org.apache.shenyu.common.constant.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -41,6 +46,8 @@ public class HierarchicalWheelTimer implements Timer {
private static final AtomicIntegerFieldUpdater<HierarchicalWheelTimer>
WORKER_STATE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(HierarchicalWheelTimer.class,
"workerState");
+ private static final Logger LOG =
LoggerFactory.getLogger(HierarchicalWheelTimer.class);
+
private final ExecutorService taskExecutor;
private final DelayQueue<TimerTaskList> delayQueue = new DelayQueue<>();
@@ -80,9 +87,9 @@ public class HierarchicalWheelTimer implements Timer {
final Long tickMs,
final Integer wheelSize,
final Long startMs) {
- ThreadFactory threadFactory = ShenyuThreadFactory.create(executorName,
false);
- taskExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(), threadFactory);
+ ThreadFactory threadFactory = ShenyuThreadFactory.create(executorName,
true);
+ taskExecutor = new ShenyuThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS,
+ new MemorySafeTaskQueue<Runnable>(Constants.THE_256_MB),
threadFactory, new ThreadPoolExecutor.AbortPolicy());
workerThread = threadFactory.newThread(new Worker(this));
timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter,
delayQueue);
}
@@ -106,7 +113,11 @@ public class HierarchicalWheelTimer implements Timer {
private void addTimerTaskEntry(final TimerTaskList.TimerTaskEntry
timerTaskEntry) {
if (!timingWheel.add(timerTaskEntry)) {
if (!timerTaskEntry.cancelled()) {
- taskExecutor.submit(() ->
timerTaskEntry.getTimerTask().run(timerTaskEntry));
+ try {
+ taskExecutor.submit(() ->
timerTaskEntry.getTimerTask().run(timerTaskEntry));
+ } catch (RejectedExecutionException e) {
+ LOG.warn("Dropping the expired timer task because the
timer task queue has no remaining memory", e);
+ }
}
}
}
diff --git
a/shenyu-common/src/test/java/org/apache/shenyu/common/timer/HierarchicalWheelTimerTest.java
b/shenyu-common/src/test/java/org/apache/shenyu/common/timer/HierarchicalWheelTimerTest.java
index a910ea3abf..3a835e3aae 100644
---
a/shenyu-common/src/test/java/org/apache/shenyu/common/timer/HierarchicalWheelTimerTest.java
+++
b/shenyu-common/src/test/java/org/apache/shenyu/common/timer/HierarchicalWheelTimerTest.java
@@ -17,12 +17,16 @@
package org.apache.shenyu.common.timer;
+import org.apache.shenyu.common.concurrent.MemorySafeTaskQueue;
+import org.apache.shenyu.common.concurrent.ShenyuThreadPoolExecutor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -128,6 +132,44 @@ public class HierarchicalWheelTimerTest {
}));
}
+ /**
+ * Test that the worker and task threads are daemons so the timer cannot
block JVM shutdown.
+ *
+ * @throws Exception reflection exception
+ */
+ @Test
+ public void testTimerThreadsAreDaemon() throws Exception {
+ CountDownLatch taskLatch = new CountDownLatch(1);
+ AtomicBoolean taskThreadDaemon = new AtomicBoolean();
+ timer.add(new TimerTask(1) {
+ @Override
+ public void run(final TaskEntity taskEntity) {
+ taskThreadDaemon.set(Thread.currentThread().isDaemon());
+ taskLatch.countDown();
+ }
+ });
+ assertTrue(taskLatch.await(5, TimeUnit.SECONDS));
+
+ Field workerThreadField =
HierarchicalWheelTimer.class.getDeclaredField("workerThread");
+ workerThreadField.setAccessible(true);
+ Thread workerThread = (Thread) workerThreadField.get(timer);
+ assertTrue(workerThread.isDaemon());
+ assertTrue(taskThreadDaemon.get());
+ }
+
+ /**
+ * Test that the task executor queue is bounded by available memory
instead of growing without limit.
+ *
+ * @throws Exception reflection exception
+ */
+ @Test
+ public void testTaskExecutorQueueIsMemorySafe() throws Exception {
+ Field taskExecutorField =
HierarchicalWheelTimer.class.getDeclaredField("taskExecutor");
+ taskExecutorField.setAccessible(true);
+ ShenyuThreadPoolExecutor executor = (ShenyuThreadPoolExecutor)
taskExecutorField.get(timer);
+ assertTrue(executor.getQueue() instanceof MemorySafeTaskQueue);
+ }
+
/**
* Test list foreach.
*/