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

Reply via email to