You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2018/11/08 10:17:44 UTC

svn commit: r1846116 - in /tomcat/trunk: java/org/apache/catalina/ java/org/apache/catalina/core/ java/org/apache/tomcat/util/threads/ test/org/apache/catalina/mbeans/ webapps/docs/ webapps/docs/config/

Author: remm
Date: Thu Nov  8 10:17:43 2018
New Revision: 1846116

URL: http://svn.apache.org/viewvc?rev=1846116&view=rev
Log:
Add a scheduled executor service to the Service, which can be used to process utility tasks including periodic ones.
Add a simple wrapper to prevent random lifecycle and configuration operations.
Add a bean for it.

Added:
    tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java   (with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/Service.java
    tomcat/trunk/java/org/apache/catalina/core/StandardService.java
    tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/service.xml

Modified: tomcat/trunk/java/org/apache/catalina/Service.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Service.java?rev=1846116&r1=1846115&r2=1846116&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Service.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Service.java Thu Nov  8 10:17:43 2018
@@ -18,6 +18,8 @@
 
 package org.apache.catalina;
 
+import java.util.concurrent.ScheduledExecutorService;
+
 import org.apache.catalina.connector.Connector;
 import org.apache.catalina.mapper.Mapper;
 
@@ -96,6 +98,20 @@ public interface Service extends Lifecyc
     public String getDomain();
 
 
+    /**
+     * Get the utility thread count.
+     * @return the thread count
+     */
+    public int getUtilityThreads();
+
+
+    /**
+     * Set the utility thread count.
+     * @param utilityThreads the new thread count
+     */
+    public void setUtilityThreads(int utilityThreads);
+
+
     // --------------------------------------------------------- Public Methods
 
     /**
@@ -151,4 +167,10 @@ public interface Service extends Lifecyc
      * @return the mapper associated with this Service.
      */
     Mapper getMapper();
+
+    /**
+     * @return the utility executor managed by the Service.
+     */
+    ScheduledExecutorService getUtilityExecutor();
+
 }

Modified: tomcat/trunk/java/org/apache/catalina/core/StandardService.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardService.java?rev=1846116&r1=1846115&r2=1846116&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardService.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardService.java Thu Nov  8 10:17:43 2018
@@ -20,6 +20,11 @@ package org.apache.catalina.core;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.util.ArrayList;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.management.ObjectName;
 
@@ -85,10 +90,25 @@ public class StandardService extends Lif
     private final Object connectorsLock = new Object();
 
     /**
-     *
+     * The list of executors held by the service.
      */
     protected final ArrayList<Executor> executors = new ArrayList<>();
 
+    /**
+     * The number of threads available to process utility tasks in this service.
+     */
+    protected int utilityThreads = 0;
+
+    /**
+     * Utility executor with scheduling capabilities.
+     */
+    private ScheduledThreadPoolExecutor utilityExecutor = null;
+
+    /**
+     * Utility executor wrapper.
+     */
+    private ScheduledExecutorService utilityExecutorWrapper = null;
+
     private Engine engine = null;
 
     private ClassLoader parentClassLoader = null;
@@ -202,8 +222,65 @@ public class StandardService extends Lif
     }
 
 
+    @Override
+    public int getUtilityThreads() {
+        return utilityThreads;
+    }
+
+
+    /**
+     * Handles the special values.
+     */
+    private int getUtilityThreadsInternal() {
+        int result = getUtilityThreads();
+        if (result > 0) {
+            return result;
+        }
+
+        // Zero == Runtime.getRuntime().availableProcessors()
+        // -ve  == Runtime.getRuntime().availableProcessors() + value
+        // These two are the same
+        result = (Runtime.getRuntime().availableProcessors() / 2) + result;
+        if (result < 1) {
+            result = 1;
+        }
+        return result;
+    }
+
+    @Override
+    public void setUtilityThreads(int utilityThreads) {
+        if (utilityThreads < getUtilityThreadsInternal()) {
+            return;
+        }
+        int oldUtilityThreads = this.utilityThreads;
+        this.utilityThreads = utilityThreads;
+
+        // Use local copies to ensure thread safety
+        if (oldUtilityThreads != utilityThreads && utilityExecutor != null) {
+            reconfigureUtilityExecutor(getUtilityThreadsInternal());
+        }
+    }
+
+
+    private synchronized void reconfigureUtilityExecutor(int threads) {
+        if (utilityExecutor != null) {
+            utilityExecutor.setMaximumPoolSize(threads);
+        } else {
+            ScheduledThreadPoolExecutor scheduledThreadPoolExecutor =
+                    new ScheduledThreadPoolExecutor(1, new UtilityThreadFactory(getName() + "-utility-"));
+            scheduledThreadPoolExecutor.setMaximumPoolSize(threads);
+            scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS);
+            scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);
+            scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+            utilityExecutor = scheduledThreadPoolExecutor;
+            utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor);
+        }
+    }
+
+
     // --------------------------------------------------------- Public Methods
 
+
     /**
      * Add a new Connector to the set of defined Connectors, and associate it
      * with this Service's Container.
@@ -500,6 +577,7 @@ public class StandardService extends Lif
                 executor.stop();
             }
         }
+
     }
 
 
@@ -512,6 +590,9 @@ public class StandardService extends Lif
 
         super.initInternal();
 
+        reconfigureUtilityExecutor(getUtilityThreadsInternal());
+        register(utilityExecutor, "type=UtilityExecutor");
+
         if (engine != null) {
             engine.init();
         }
@@ -556,6 +637,12 @@ public class StandardService extends Lif
             engine.destroy();
         }
 
+        if (utilityExecutor != null) {
+            utilityExecutor.shutdownNow();
+            unregister("type=UtilityExecutor");
+            utilityExecutor = null;
+        }
+
         super.destroyInternal();
     }
 
@@ -613,4 +700,29 @@ public class StandardService extends Lif
     public final String getObjectNameKeyProperties() {
         return "type=Service";
     }
+
+    private static class UtilityThreadFactory implements ThreadFactory {
+        private final ThreadGroup group;
+        private final AtomicInteger threadNumber = new AtomicInteger(1);
+        private final String namePrefix;
+
+        public UtilityThreadFactory(String namePrefix) {
+            SecurityManager s = System.getSecurityManager();
+            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+            this.namePrefix = namePrefix;
+        }
+
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement());
+            thread.setDaemon(true);
+            return thread;
+        }
+    }
+
+    @Override
+    public ScheduledExecutorService getUtilityExecutor() {
+        return utilityExecutorWrapper;
+    }
+
 }

Added: tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java?rev=1846116&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java Thu Nov  8 10:17:43 2018
@@ -0,0 +1,141 @@
+/*
+ * 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.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Class which wraps a ScheduledExecutorService, while preventing
+ * lifecycle and configuration operations.
+ */
+public class ScheduledThreadPoolExecutor implements ScheduledExecutorService {
+
+    protected final ScheduledExecutorService executor;
+
+    /**
+     * Builds a wrapper for the given executor.
+     * @param executor the wrapped executor
+     */
+    public ScheduledThreadPoolExecutor(ScheduledExecutorService executor) {
+        this.executor = executor;
+    }
+
+    @Override
+    public void shutdown() {
+        // Do nothing
+    }
+
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return executor.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return executor.isTerminated();
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+            throws InterruptedException {
+        return executor.awaitTermination(timeout, unit);
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+        return executor.submit(task);
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+        return executor.submit(task, result);
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+        return executor.submit(task);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException {
+        return executor.invokeAll(tasks);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+            TimeUnit unit) throws InterruptedException {
+        return executor.invokeAll(tasks, timeout, unit);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException, ExecutionException {
+        return executor.invokeAny(tasks);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
+            long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        return executor.invokeAny(tasks, timeout, unit);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        executor.execute(command);
+    }
+
+    @Override
+    public ScheduledFuture<?> schedule(Runnable command, long delay,
+            TimeUnit unit) {
+        return executor.schedule(command, delay, unit);
+    }
+
+    @Override
+    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay,
+            TimeUnit unit) {
+        return executor.schedule(callable, delay, unit);
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
+            long initialDelay, long period, TimeUnit unit) {
+        return executor.scheduleAtFixedRate(command, initialDelay, period, unit);
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
+            long initialDelay, long delay, TimeUnit unit) {
+        return executor.scheduleWithFixedDelay(command, initialDelay, delay, unit);
+    }
+
+}

Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java?rev=1846116&r1=1846115&r2=1846116&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java (original)
+++ tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java Thu Nov  8 10:17:43 2018
@@ -72,6 +72,7 @@ public class TestRegistration extends To
             "Tomcat:type=Server",
             "Tomcat:type=Service",
             "Tomcat:type=StringCache",
+            "Tomcat:type=UtilityExecutor",
             "Tomcat:type=Valve,name=StandardEngineValve",
         };
     }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1846116&r1=1846115&r2=1846116&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Thu Nov  8 10:17:43 2018
@@ -64,6 +64,10 @@
         Include German translations for the Manager application in the standard
         Tomcat distribution. (markt)
       </fix>
+      <add>
+        Add a scheduled executor to the Service, which can be used to
+        process periodic utility tasks. (remm)
+      </add>
     </changelog>
   </subsection>
 </section>

Modified: tomcat/trunk/webapps/docs/config/service.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/service.xml?rev=1846116&r1=1846115&r2=1846116&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/service.xml (original)
+++ tomcat/trunk/webapps/docs/config/service.xml Thu Nov  8 10:17:43 2018
@@ -68,6 +68,17 @@
       must be unique.</p>
     </attribute>
 
+    <attribute name="startStopThreads" required="false">
+      <p>The number of threads this <strong>Service</strong> will use for
+      various utility tasks, including recurring ones. The special value
+      of 0 will result in the value of
+      <code>Runtime.getRuntime().availableProcessors()/2</code> being
+      used. Negative values will result in
+      <code>Runtime.getRuntime().availableProcessors()/2 + value</code> being
+      used unless this is less than 1 in which case 1 thread will be used.
+      </p>
+    </attribute>
+
   </attributes>
 
   </subsection>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r1846116 - in /tomcat/trunk: java/org/apache/catalina/ java/org/apache/catalina/core/ java/org/apache/tomcat/util/threads/ test/org/apache/catalina/mbeans/ webapps/docs/ webapps/docs/config/

Posted by Rémy Maucherat <re...@apache.org>.
On Fri, Nov 23, 2018 at 1:05 PM Mark Thomas <ma...@apache.org> wrote:

> On 08/11/2018 10:17, remm@apache.org wrote:
> > Author: remm
> > Date: Thu Nov  8 10:17:43 2018
> > New Revision: 1846116
> >
> > URL: http://svn.apache.org/viewvc?rev=1846116&view=rev
> > Log:
> > Add a scheduled executor service to the Service, which can be used to
> process utility tasks including periodic ones.
> > Add a simple wrapper to prevent random lifecycle and configuration
> operations.
> > Add a bean for it.
>
> <snip/>
>
> > Modified: tomcat/trunk/java/org/apache/catalina/core/StandardService.java
> > URL:
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardService.java?rev=1846116&r1=1846115&r2=1846116&view=diff
>
> <snip/>
>
> > +    private synchronized void reconfigureUtilityExecutor(int threads) {
> > +        if (utilityExecutor != null) {
> > +            utilityExecutor.setMaximumPoolSize(threads);
>
> I'm seeing a SpotBugs warning on the above line that calling
> setMaximumPoolSize() is a NO-OP. From the Javadoc:
>
> <quote>
> In particular, because it acts as a fixed-sized pool using {@code
> corePoolSize} threads and an unbounded queue, adjustments to {@code
> maximumPoolSize} have no useful effect.
> </quote>
>
> Should this be calling setCorePoolSize() ?
>

Ok, I will make adjustments then. Another bad idea as well: using
allowCoreThreadTimeOut, all is does is endlessly stopping and restarting
immediately the core threads.

Rémy

Re: svn commit: r1846116 - in /tomcat/trunk: java/org/apache/catalina/ java/org/apache/catalina/core/ java/org/apache/tomcat/util/threads/ test/org/apache/catalina/mbeans/ webapps/docs/ webapps/docs/config/

Posted by Mark Thomas <ma...@apache.org>.
On 08/11/2018 10:17, remm@apache.org wrote:
> Author: remm
> Date: Thu Nov  8 10:17:43 2018
> New Revision: 1846116
> 
> URL: http://svn.apache.org/viewvc?rev=1846116&view=rev
> Log:
> Add a scheduled executor service to the Service, which can be used to process utility tasks including periodic ones.
> Add a simple wrapper to prevent random lifecycle and configuration operations.
> Add a bean for it.

<snip/>

> Modified: tomcat/trunk/java/org/apache/catalina/core/StandardService.java
> URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardService.java?rev=1846116&r1=1846115&r2=1846116&view=diff

<snip/>

> +    private synchronized void reconfigureUtilityExecutor(int threads) {
> +        if (utilityExecutor != null) {
> +            utilityExecutor.setMaximumPoolSize(threads);

I'm seeing a SpotBugs warning on the above line that calling
setMaximumPoolSize() is a NO-OP. From the Javadoc:

<quote>
In particular, because it acts as a fixed-sized pool using {@code
corePoolSize} threads and an unbounded queue, adjustments to {@code
maximumPoolSize} have no useful effect.
</quote>

Should this be calling setCorePoolSize() ?

Mark

> +        } else {
> +            ScheduledThreadPoolExecutor scheduledThreadPoolExecutor =
> +                    new ScheduledThreadPoolExecutor(1, new UtilityThreadFactory(getName() + "-utility-"));
> +            scheduledThreadPoolExecutor.setMaximumPoolSize(threads);
> +            scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS);
> +            scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true);
> +            scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
> +            utilityExecutor = scheduledThreadPoolExecutor;
> +            utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor);
> +        }

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org