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.
      */

Reply via email to