Author: markt Date: Mon Mar 6 11:54:11 2017 New Revision: 1785637 URL: http://svn.apache.org/viewvc?rev=1785637&view=rev Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=60623 When startStopThreads is 1 (or a special value that is equivalent to 1) then rather than using an ExecutorService to start the children of the current component, the children will be started on the current thread.
Added: tomcat/trunk/java/org/apache/tomcat/util/threads/InlineExecutorService.java Modified: tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/engine.xml tomcat/trunk/webapps/docs/config/host.xml Modified: tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java?rev=1785637&r1=1785636&r2=1785637&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java Mon Mar 6 11:54:11 2017 @@ -28,6 +28,7 @@ import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; @@ -65,6 +66,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.res.StringManager; +import org.apache.tomcat.util.threads.InlineExecutorService; /** @@ -276,7 +278,7 @@ public abstract class ContainerBase exte * children associated with this container. */ private int startStopThreads = 1; - protected ThreadPoolExecutor startStopExecutor; + protected ExecutorService startStopExecutor; // ------------------------------------------------------------- Properties @@ -309,14 +311,12 @@ public abstract class ContainerBase exte @Override public void setStartStopThreads(int startStopThreads) { + int oldStartStopThreads = this.startStopThreads; this.startStopThreads = startStopThreads; // Use local copies to ensure thread safety - ThreadPoolExecutor executor = startStopExecutor; - if (executor != null) { - int newThreads = getStartStopThreadsInternal(); - executor.setMaximumPoolSize(newThreads); - executor.setCorePoolSize(newThreads); + if (oldStartStopThreads != startStopThreads && startStopExecutor != null) { + reconfigureStartStopExecutor(getStartStopThreadsInternal()); } } @@ -893,17 +893,37 @@ public abstract class ContainerBase exte @Override protected void initInternal() throws LifecycleException { - BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>(); - startStopExecutor = new ThreadPoolExecutor( - getStartStopThreadsInternal(), - getStartStopThreadsInternal(), 10, TimeUnit.SECONDS, - startStopQueue, - new StartStopThreadFactory(getName() + "-startStop-")); - startStopExecutor.allowCoreThreadTimeOut(true); + reconfigureStartStopExecutor(getStartStopThreadsInternal()); super.initInternal(); } + /* + * Implementation note: If there is a demand for more control than this then + * it is likely that the best solution will be to reference an external + * executor. + */ + private void reconfigureStartStopExecutor(int threads) { + if (threads == 1) { + if (!(startStopExecutor instanceof InlineExecutorService)) { + startStopExecutor = new InlineExecutorService(); + } + } else { + if (startStopExecutor instanceof ThreadPoolExecutor) { + ((ThreadPoolExecutor) startStopExecutor).setMaximumPoolSize(threads); + ((ThreadPoolExecutor) startStopExecutor).setCorePoolSize(threads); + } else { + BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>(); + ThreadPoolExecutor tpe = new ThreadPoolExecutor(threads, threads, 10, + TimeUnit.SECONDS, startStopQueue, + new StartStopThreadFactory(getName() + "-startStop-")); + tpe.allowCoreThreadTimeOut(true); + startStopExecutor = tpe; + } + } + } + + /** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. Added: tomcat/trunk/java/org/apache/tomcat/util/threads/InlineExecutorService.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/InlineExecutorService.java?rev=1785637&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/threads/InlineExecutorService.java (added) +++ tomcat/trunk/java/org/apache/tomcat/util/threads/InlineExecutorService.java Mon Mar 6 11:54:11 2017 @@ -0,0 +1,84 @@ +/* + * 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. + */ +package org.apache.tomcat.util.threads; + +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +public class InlineExecutorService extends AbstractExecutorService { + + private volatile boolean shutdown; + private volatile boolean taskRunning; + private volatile boolean terminated; + + private final Object lock = new Object(); + + @Override + public void shutdown() { + shutdown = true; + synchronized (lock) { + terminated = !taskRunning; + } + } + + @Override + public List<Runnable> shutdownNow() { + shutdown(); + return null; + } + + @Override + public boolean isShutdown() { + return shutdown; + } + + @Override + public boolean isTerminated() { + return terminated; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + synchronized (lock) { + if (terminated) { + return true; + } + this.wait(unit.toMillis(timeout)); + return terminated; + } + } + + @Override + public void execute(Runnable command) { + synchronized (lock) { + if (shutdown) { + throw new RejectedExecutionException(); + } + taskRunning = true; + } + command.run(); + synchronized (lock) { + taskRunning = false; + if (shutdown) { + terminated = true; + this.notifyAll(); + } + } + } +} Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1785637&r1=1785636&r2=1785637&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Mar 6 11:54:11 2017 @@ -71,6 +71,12 @@ implementation when multiple threads are managing objects and need to reference the annotation cache. (markt) </fix> + <fix> + <bug>60623</bug>: When startStopThreads is 1 (or a special value that + is equivalent to 1) then rather than using an + <code>ExecutorService</code> to start the children of the current + component, the children will be started on the current thread. (markt) + </fix> <scode> <bug>60674</bug>: Remove <code>final</code> marker from <code>CorsFilter</code> to enable sub-classing. (markt) Modified: tomcat/trunk/webapps/docs/config/engine.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/engine.xml?rev=1785637&r1=1785636&r2=1785637&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/engine.xml (original) +++ tomcat/trunk/webapps/docs/config/engine.xml Mon Mar 6 11:54:11 2017 @@ -116,7 +116,9 @@ Negative values will result in <code>Runtime.getRuntime().availableProcessors() + value</code> being used unless this is less than 1 in which case 1 thread will be used. If - not specified, the default value of 1 will be used. </p> + not specified, the default value of 1 will be used. If 1 thread is + used then rather than using an <code>ExecutorService</code> the current + thread will be used.</p> </attribute> </attributes> Modified: tomcat/trunk/webapps/docs/config/host.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1785637&r1=1785636&r2=1785637&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/host.xml (original) +++ tomcat/trunk/webapps/docs/config/host.xml Mon Mar 6 11:54:11 2017 @@ -208,7 +208,9 @@ Negative values will result in <code>Runtime.getRuntime().availableProcessors() + value</code> being used unless this is less than 1 in which case 1 thread will be used. If - not specified, the default value of 1 will be used.</p> + not specified, the default value of 1 will be used. If 1 thread is + used then rather than using an <code>ExecutorService</code> the current + thread will be used.</p> </attribute> <attribute name="undeployOldVersions" required="false"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org