This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 93dea1dc4db626c072526e7587bb41ca95bef0bd Author: opencode <[email protected]> AuthorDate: Fri Oct 16 15:31:00 2026 +0200 Shut down the internal virtual thread executor when the endpoint stops shutdownExecutor() only shut down the thread pool branch, leaving a virtual thread executor accepting tasks submitted through a reference captured before the stop. Shut it down as well, so such submissions are rejected, as they already are with the thread pool executor. --- .../apache/tomcat/util/net/AbstractEndpoint.java | 6 +++ .../tomcat/util/net/TestAbstractEndpoint.java | 44 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java index 3a9335085d..9af6762bd2 100644 --- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java +++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java @@ -1993,6 +1993,12 @@ public abstract class AbstractEndpoint<S, U> { } TaskQueue queue = (TaskQueue) tpe.getQueue(); queue.setParent(null); + } else if (executor instanceof VirtualThreadExecutor virtualThreadExecutor) { + // The virtual thread executor accepts new tasks until it is shut + // down. Shut it down, so tasks submitted via a reference captured + // before this point are rejected, as they are with the thread + // pool executor. + virtualThreadExecutor.shutdownNow(); } } } diff --git a/test/org/apache/tomcat/util/net/TestAbstractEndpoint.java b/test/org/apache/tomcat/util/net/TestAbstractEndpoint.java index 055cab7512..4b06e254c9 100644 --- a/test/org/apache/tomcat/util/net/TestAbstractEndpoint.java +++ b/test/org/apache/tomcat/util/net/TestAbstractEndpoint.java @@ -16,12 +16,17 @@ */ package org.apache.tomcat.util.net; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import org.junit.Assert; import org.junit.Test; +import org.apache.tomcat.util.threads.VirtualThreadExecutor; + public class TestAbstractEndpoint { @Test @@ -65,4 +70,43 @@ public class TestAbstractEndpoint { executor.shutdown(); } } + + /* + * The virtual thread executor accepts tasks until it is shut down. A reference captured before the endpoint + * stopped must be rejected, as it is when the executor is a thread pool. + */ + @Test + public void testCreatedVirtualThreadExecutorIsShutDownOnStop() throws Exception { + NioEndpoint endpoint = new NioEndpoint(); + endpoint.setUseVirtualThreads(true); + endpoint.createExecutor(); + Executor executor = endpoint.getExecutor(); + Assert.assertTrue(executor instanceof VirtualThreadExecutor); + + endpoint.shutdownExecutor(); + + Assert.assertTrue(((ExecutorService) executor).isShutdown()); + try { + executor.execute(() -> { + }); + Assert.fail("Expected RejectedExecutionException"); + } catch (RejectedExecutionException e) { + // Expected. + } + } + + @Test + public void testExternalVirtualThreadExecutorIsNotShutDownOnStop() throws Exception { + NioEndpoint endpoint = new NioEndpoint(); + VirtualThreadExecutor executor = new VirtualThreadExecutor("test-external-"); + endpoint.setExecutor(executor); + try { + endpoint.shutdownExecutor(); + + // Externally provided executors are owned by the caller + Assert.assertFalse(executor.isShutdown()); + } finally { + executor.shutdownNow(); + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
