Author: remm
Date: Thu Nov  8 10:29:24 2018
New Revision: 1846118

URL: http://svn.apache.org/viewvc?rev=1846118&view=rev
Log:
Refactor container level threads using the Service executor.
Add additional monitoring of the main container background processor to 
reschedule it when needed.

Modified:
    tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java
    tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
    tomcat/trunk/webapps/docs/changelog.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=1846118&r1=1846117&r2=1846118&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/ContainerBase.java Thu Nov  8 
10:29:24 2018
@@ -24,16 +24,13 @@ import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -55,6 +52,7 @@ import org.apache.catalina.LifecycleStat
 import org.apache.catalina.Loader;
 import org.apache.catalina.Pipeline;
 import org.apache.catalina.Realm;
+import org.apache.catalina.Service;
 import org.apache.catalina.Valve;
 import org.apache.catalina.Wrapper;
 import org.apache.catalina.connector.Request;
@@ -172,6 +170,12 @@ public abstract class ContainerBase exte
 
 
     /**
+     * The future allowing control of the background processor.
+     */
+    protected ScheduledFuture<?> backgroundProcessorFuture;
+    protected ScheduledFuture<?> monitorFuture;
+
+    /**
      * The container event listeners for this Container. Implemented as a
      * CopyOnWriteArrayList since listeners may invoke methods to add/remove
      * themselves or other listeners and with a ReadWriteLock that would 
trigger
@@ -254,18 +258,6 @@ public abstract class ContainerBase exte
 
 
     /**
-     * The background thread.
-     */
-    private Thread thread = null;
-
-
-    /**
-     * The background thread completion semaphore.
-     */
-    private volatile boolean threadDone = false;
-
-
-    /**
      * The access log to use for requests normally handled by this container
      * that have been handled earlier in the processing chain.
      */
@@ -288,27 +280,6 @@ public abstract class ContainerBase exte
         return startStopThreads;
     }
 
-    /**
-     * Handles the special values.
-     */
-    private int getStartStopThreadsInternal() {
-        int result = getStartStopThreads();
-
-        // Positive values are unchanged
-        if (result > 0) {
-            return result;
-        }
-
-        // Zero == Runtime.getRuntime().availableProcessors()
-        // -ve  == Runtime.getRuntime().availableProcessors() + value
-        // These two are the same
-        result = Runtime.getRuntime().availableProcessors() + result;
-        if (result < 1) {
-            result = 1;
-        }
-        return result;
-    }
-
     @Override
     public void setStartStopThreads(int startStopThreads) {
         int oldStartStopThreads = this.startStopThreads;
@@ -316,7 +287,7 @@ public abstract class ContainerBase exte
 
         // Use local copies to ensure thread safety
         if (oldStartStopThreads != startStopThreads && startStopExecutor != 
null) {
-            reconfigureStartStopExecutor(getStartStopThreadsInternal());
+            reconfigureStartStopExecutor(getStartStopThreads());
         }
     }
 
@@ -884,7 +855,7 @@ public abstract class ContainerBase exte
 
     @Override
     protected void initInternal() throws LifecycleException {
-        reconfigureStartStopExecutor(getStartStopThreadsInternal());
+        reconfigureStartStopExecutor(getStartStopThreads());
         super.initInternal();
     }
 
@@ -896,21 +867,15 @@ public abstract class ContainerBase exte
      */
     private void reconfigureStartStopExecutor(int threads) {
         if (threads == 1) {
+            // Use a fake executor
             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;
-            }
+            // Delegate utility execution to the Service
+            Service service = Container.getService(this);
+            service.setUtilityThreads(threads);
+            startStopExecutor = 
Container.getService(this).getUtilityExecutor();
         }
     }
 
@@ -968,11 +933,15 @@ public abstract class ContainerBase exte
             ((Lifecycle) pipeline).start();
         }
 
-
         setState(LifecycleState.STARTING);
 
         // Start our thread
         threadStart();
+        if (backgroundProcessorDelay > 0) {
+            monitorFuture = Container.getService(ContainerBase.this)
+                    .getUtilityExecutor().scheduleWithFixedDelay(
+                            new ContainerBackgroundProcessorMonitor(), 60, 60, 
TimeUnit.SECONDS);
+        }
     }
 
 
@@ -987,6 +956,9 @@ public abstract class ContainerBase exte
     protected synchronized void stopInternal() throws LifecycleException {
 
         // Stop our thread
+        if (monitorFuture != null) {
+            monitorFuture.cancel(true);
+        }
         threadStop();
 
         setState(LifecycleState.STOPPING);
@@ -1295,18 +1267,21 @@ public abstract class ContainerBase exte
      * session timeouts.
      */
     protected void threadStart() {
-
-        if (thread != null)
-            return;
-        if (backgroundProcessorDelay <= 0)
-            return;
-
-        threadDone = false;
-        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
-        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
-        thread.setDaemon(true);
-        thread.start();
-
+        if (backgroundProcessorDelay > 0 && getState().isAvailable()
+                && (backgroundProcessorFuture == null || 
backgroundProcessorFuture.isDone())) {
+            if (backgroundProcessorFuture != null && 
backgroundProcessorFuture.isDone()) {
+                // There was an error executing the scheduled task, get it and 
log it
+                try {
+                    backgroundProcessorFuture.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    
log.error(sm.getString("containerBase.backgroundProcess.error"), e);
+                }
+            }
+            backgroundProcessorFuture = 
Container.getService(this).getUtilityExecutor()
+                    .scheduleWithFixedDelay(new ContainerBackgroundProcessor(),
+                            backgroundProcessorDelay, backgroundProcessorDelay,
+                            TimeUnit.SECONDS);
+        }
     }
 
 
@@ -1315,20 +1290,10 @@ public abstract class ContainerBase exte
      * session timeouts.
      */
     protected void threadStop() {
-
-        if (thread == null)
-            return;
-
-        threadDone = true;
-        thread.interrupt();
-        try {
-            thread.join();
-        } catch (InterruptedException e) {
-            // Ignore
+        if (backgroundProcessorFuture != null) {
+            backgroundProcessorFuture.cancel(true);
+            backgroundProcessorFuture = null;
         }
-
-        thread = null;
-
     }
 
 
@@ -1347,40 +1312,26 @@ public abstract class ContainerBase exte
         return sb.toString();
     }
 
+    // ------------------------------- ContainerBackgroundProcessor Inner Class
 
-    // -------------------------------------- ContainerExecuteDelay Inner Class
+    protected class ContainerBackgroundProcessorMonitor implements Runnable {
+        @Override
+        public void run() {
+            if (getState().isAvailable()) {
+                threadStart();
+            }
+        }
+    }
 
     /**
-     * Private thread class to invoke the backgroundProcess method
+     * Private runnable class to invoke the backgroundProcess method
      * of this container and its children after a fixed delay.
      */
     protected class ContainerBackgroundProcessor implements Runnable {
 
         @Override
         public void run() {
-            Throwable t = null;
-            String unexpectedDeathMessage = sm.getString(
-                    "containerBase.backgroundProcess.unexpectedThreadDeath",
-                    Thread.currentThread().getName());
-            try {
-                while (!threadDone) {
-                    try {
-                        Thread.sleep(backgroundProcessorDelay * 1000L);
-                    } catch (InterruptedException e) {
-                        // Ignore
-                    }
-                    if (!threadDone) {
-                        processChildren(ContainerBase.this);
-                    }
-                }
-            } catch (RuntimeException|Error e) {
-                t = e;
-                throw e;
-            } finally {
-                if (!threadDone) {
-                    log.error(unexpectedDeathMessage, t);
-                }
-            }
+            processChildren(ContainerBase.this);
         }
 
         protected void processChildren(Container container) {
@@ -1407,17 +1358,17 @@ public abstract class ContainerBase exte
                 }
             } catch (Throwable t) {
                 ExceptionUtils.handleThrowable(t);
-                log.error("Exception invoking periodic operation: ", t);
+                
log.error(sm.getString("containerBase.backgroundProcess.error"), t);
             } finally {
                 if (container instanceof Context) {
                     ((Context) container).unbind(false, originalClassLoader);
-               }
+                }
             }
         }
     }
 
 
-    // ----------------------------- Inner classes used with start/stop 
Executor
+    // ---------------------------- Inner classes used with start/stop Executor
 
     private static class StartChild implements Callable<Void> {
 
@@ -1451,22 +1402,4 @@ public abstract class ContainerBase exte
         }
     }
 
-    private static class StartStopThreadFactory implements ThreadFactory {
-        private final ThreadGroup group;
-        private final AtomicInteger threadNumber = new AtomicInteger(1);
-        private final String namePrefix;
-
-        public StartStopThreadFactory(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;
-        }
-    }
 }

Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1846118&r1=1846117&r2=1846118&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties [UTF-8] 
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties [UTF-8] 
Thu Nov  8 10:29:24 2018
@@ -97,6 +97,7 @@ containerBase.backgroundProcess.cluster=
 containerBase.backgroundProcess.realm=Exception processing realm [{0}] 
background process
 containerBase.backgroundProcess.valve=Exception processing valve [{0}] 
background process
 containerBase.backgroundProcess.unexpectedThreadDeath=Unexpected death of 
background thread [{0}]
+containerBase.backgroundProcess.error=Exception processing background thread
 filterChain.filter=Filter execution threw an exception
 filterChain.servlet=Servlet execution threw an exception
 jreLeakListener.gcDaemonFail=Failed to trigger creation of the GC Daemon 
thread during Tomcat start to prevent possible memory leaks. This is expected 
on non-Sun JVMs.

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1846118&r1=1846117&r2=1846118&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Thu Nov  8 10:29:24 2018
@@ -68,6 +68,13 @@
         Add a scheduled executor to the Service, which can be used to
         process periodic utility tasks. (remm)
       </add>
+      <update>
+        Refactor container background processor using the Service executor, and
+        add monitoring to reschedule it in case of an unexpected error. (remm)
+      </update>
+      <update>
+        Refactor parallel deployment threads using the Service executor. (remm)
+      </update>
     </changelog>
   </subsection>
 </section>

Modified: tomcat/trunk/webapps/docs/config/host.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1846118&r1=1846117&r2=1846118&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/host.xml (original)
+++ tomcat/trunk/webapps/docs/config/host.xml Thu Nov  8 10:29:24 2018
@@ -203,11 +203,10 @@
         child <a href="context.html">Context</a> elements in parallel. The same
         thread pool will be used to deploy new
         <a href="context.html">Context</a>s if automatic deployment is being
-        used. The special value of 0 will result in the value of
-        <code>Runtime.getRuntime().availableProcessors()</code> being used.
-        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
+        used.
+        As the thread pool is shared at the service level, if more than one
+        host specifies this setting, only the maximum value will apply and will
+        be used for all except for the special value 1. If
         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>



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

Reply via email to