This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 0640663aa9 Implement ExecutorService 0640663aa9 is described below commit 0640663aa94ea79cd70729fce23ac9378a82b57e Author: remm <r...@apache.org> AuthorDate: Fri Mar 1 09:50:55 2024 +0100 Implement ExecutorService This allows better interactions with NIO2. BZ68692 --- .../catalina/core/StandardThreadExecutor.java | 121 +++++++++++++++++++- .../core/StandardVirtualThreadExecutor.java | 122 ++++++++++++++++++++- .../apache/tomcat/util/net/LocalStrings.properties | 1 + java/org/apache/tomcat/util/net/Nio2Endpoint.java | 2 + .../util/threads/ScheduledThreadPoolExecutor.java | 3 +- webapps/docs/changelog.xml | 7 +- 6 files changed, 251 insertions(+), 5 deletions(-) diff --git a/java/org/apache/catalina/core/StandardThreadExecutor.java b/java/org/apache/catalina/core/StandardThreadExecutor.java index 4973886889..0974260b0a 100644 --- a/java/org/apache/catalina/core/StandardThreadExecutor.java +++ b/java/org/apache/catalina/core/StandardThreadExecutor.java @@ -16,7 +16,15 @@ */ package org.apache.catalina.core; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.catalina.Executor; import org.apache.catalina.LifecycleException; @@ -28,7 +36,7 @@ import org.apache.tomcat.util.threads.TaskQueue; import org.apache.tomcat.util.threads.TaskThreadFactory; import org.apache.tomcat.util.threads.ThreadPoolExecutor; -public class StandardThreadExecutor extends LifecycleMBeanBase implements Executor, ResizableExecutor { +public class StandardThreadExecutor extends LifecycleMBeanBase implements Executor, ExecutorService, ResizableExecutor { protected static final StringManager sm = StringManager.getManager(StandardThreadExecutor.class); @@ -304,4 +312,115 @@ public class StandardThreadExecutor extends LifecycleMBeanBase implements Execut protected String getObjectNameKeyProperties() { return "type=Executor,name=" + getName(); } + + + @Override + public void shutdown() { + // Controlled by Lifecycle instead + } + + + @Override + public List<Runnable> shutdownNow() { + // Controlled by Lifecycle instead + return Collections.emptyList(); + } + + + @Override + public boolean isShutdown() { + if (executor != null) { + return executor.isShutdown(); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public boolean isTerminated() { + if (executor != null) { + return executor.isTerminated(); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return false; + } + + + @Override + public <T> Future<T> submit(Callable<T> task) { + if (executor != null) { + return executor.submit(task); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> Future<T> submit(Runnable task, T result) { + if (executor != null) { + return executor.submit(task, result); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public Future<?> submit(Runnable task) { + if (executor != null) { + return executor.submit(task); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { + if (executor != null) { + return executor.invokeAll(tasks); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + if (executor != null) { + return executor.invokeAll(tasks, timeout, unit); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { + if (executor != null) { + return executor.invokeAny(tasks); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (executor != null) { + return executor.invokeAny(tasks, timeout, unit); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } } diff --git a/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java index eb6255f939..0c271419ef 100644 --- a/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java +++ b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java @@ -16,7 +16,15 @@ */ package org.apache.catalina.core; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.catalina.Executor; import org.apache.catalina.LifecycleException; @@ -29,12 +37,12 @@ import org.apache.tomcat.util.threads.VirtualThreadExecutor; /** * An executor that uses a new virtual thread for each task. */ -public class StandardVirtualThreadExecutor extends LifecycleMBeanBase implements Executor { +public class StandardVirtualThreadExecutor extends LifecycleMBeanBase implements Executor, ExecutorService { private static final StringManager sm = StringManager.getManager(StandardVirtualThreadExecutor.class); private String name; - private java.util.concurrent.Executor executor; + private java.util.concurrent.ExecutorService executor; private String namePrefix = "tomcat-virt-"; public void setName(String name) { @@ -100,4 +108,114 @@ public class StandardVirtualThreadExecutor extends LifecycleMBeanBase implements protected String getObjectNameKeyProperties() { return "type=Executor,name=" + getName(); } + + @Override + public void shutdown() { + // Controlled by Lifecycle instead + } + + + @Override + public List<Runnable> shutdownNow() { + // Controlled by Lifecycle instead + return Collections.emptyList(); + } + + + @Override + public boolean isShutdown() { + if (executor != null) { + return executor.isShutdown(); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public boolean isTerminated() { + if (executor != null) { + return executor.isTerminated(); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return false; + } + + + @Override + public <T> Future<T> submit(Callable<T> task) { + if (executor != null) { + return executor.submit(task); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> Future<T> submit(Runnable task, T result) { + if (executor != null) { + return executor.submit(task, result); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public Future<?> submit(Runnable task) { + if (executor != null) { + return executor.submit(task); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { + if (executor != null) { + return executor.invokeAll(tasks); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + if (executor != null) { + return executor.invokeAll(tasks, timeout, unit); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { + if (executor != null) { + return executor.invokeAny(tasks); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } + + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (executor != null) { + return executor.invokeAny(tasks, timeout, unit); + } else { + throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted")); + } + } } \ No newline at end of file diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties index 20e8ed1a4d..0945510492 100644 --- a/java/org/apache/tomcat/util/net/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/LocalStrings.properties @@ -110,6 +110,7 @@ endpoint.nio.stopLatchAwaitFail=The pollers did not stop within the expected tim endpoint.nio.stopLatchAwaitInterrupted=This thread was interrupted while waiting for the pollers to stop endpoint.nio.timeoutCme=Exception during processing of timeouts. The code has been checked repeatedly and no concurrent modification has been found. If you are able to repeat this error please open a Tomcat bug and provide the steps to reproduce. endpoint.nio2.exclusiveExecutor=The NIO2 connector requires an exclusive executor to operate properly on shutdown +endpoint.nio2.executorService=The NIO2 connector requires an executor service, the internal JVM threads will be used endpoint.noSslHostConfig=No SSLHostConfig element was found with the hostName [{0}] to match the defaultSSLHostConfigName for the connector [{1}] endpoint.noSslHostName=No host name was provided for the SSL host configuration endpoint.poll.error=Unexpected poller error diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java b/java/org/apache/tomcat/util/net/Nio2Endpoint.java index b2966ac5b6..21b9f8acfb 100644 --- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java +++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java @@ -131,6 +131,8 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel,AsynchronousS } if (getExecutor() instanceof ExecutorService) { threadGroup = AsynchronousChannelGroup.withThreadPool((ExecutorService) getExecutor()); + } else { + log.info(sm.getString("endpoint.nio2.executorService")); } // AsynchronousChannelGroup needs exclusive access to its executor service if (!internalExecutor) { diff --git a/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java b/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java index 98d10cc5b4..45d9c93c50 100644 --- a/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java +++ b/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java @@ -17,6 +17,7 @@ package org.apache.tomcat.util.threads; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -50,7 +51,7 @@ public class ScheduledThreadPoolExecutor implements ScheduledExecutorService { @Override public List<Runnable> shutdownNow() { - return null; + return Collections.emptyList(); } @Override diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 816015a329..928d515442 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -116,7 +116,12 @@ <code>OutputStream</code>. Ensure use of either once the response has been recycled triggers a <code>NullPointerException</code> provided that <code>discardFacades</code> is configured with the default value of - <code>true</code>. + <code>true</code>. (markt) + </fix> + <fix> + <bug>68692</bug>: The standard thread pool implementations that are + configured using the <code>Executor</code> element now implement + <code>ExecutorService</code> for better support NIO2. (remm) </fix> </changelog> </subsection> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org