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: [email protected]
For additional commands, e-mail: [email protected]