This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new 3b82538 Improve Connector stop performance - primarily to speed up tests 3b82538 is described below commit 3b82538e9d1e5da932ba4dff13c3201ac7c39e27 Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Oct 11 16:48:26 2021 +0100 Improve Connector stop performance - primarily to speed up tests --- .../apache/tomcat/util/net/AbstractEndpoint.java | 14 ++++++++ java/org/apache/tomcat/util/net/AprEndpoint.java | 35 +++++++++++++++---- java/org/apache/tomcat/util/net/Nio2Endpoint.java | 39 +++++++++++++++++----- java/org/apache/tomcat/util/net/NioEndpoint.java | 39 +++++++++++++++++----- webapps/docs/changelog.xml | 8 +++++ 5 files changed, 113 insertions(+), 22 deletions(-) diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java index 9aad8c2..8830658 100644 --- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java +++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java @@ -1022,6 +1022,20 @@ public abstract class AbstractEndpoint<S> { waitLeft -= 5; } } + // Wait for up to 1000ms acceptor threads to unlock. Particularly + // for the unit tests, we want to exit this loop as quickly as + // possible. However, we also don't want to trigger excessive CPU + // usage if the unlock takes longer than expected. Therefore, we + // initially wait for the unlock in a tight loop but if that takes + // more than 1ms we start using short sleeps to reduce CPU usage. + long startTime = System.nanoTime(); + for (Acceptor acceptor : acceptors) { + while (startTime + 1_000_000_000 > System.nanoTime() && acceptor.getState() == AcceptorState.RUNNING) { + if (startTime + 1_000_000 < System.nanoTime()) { + Thread.sleep(1); + } + } + } } catch(Throwable t) { ExceptionUtils.handleThrowable(t); if (getLog().isDebugEnabled()) { diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java index 413a8ad..eda0abf 100644 --- a/java/org/apache/tomcat/util/net/AprEndpoint.java +++ b/java/org/apache/tomcat/util/net/AprEndpoint.java @@ -803,17 +803,40 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { public void run() { int errorDelay = 0; + long pauseStart = 0; // Loop until we receive a shutdown command while (running) { - // Loop if endpoint is paused + // Loop if endpoint is paused. + // There are two likely scenarios here. + // The first scenario is that Tomcat is shutting down. In this + // case - and particularly for the unit tests - we want to exit + // this loop as quickly as possible. The second scenario is a + // genuine pause of the connector. In this case we want to avoid + // excessive CPU usage. + // Therefore, we start with a tight loop but if there isn't a + // rapid transition to stop then sleeps are introduced. + // < 1ms - tight loop + // 1ms to 10ms - 1ms sleep + // > 10ms - 10ms sleep while (paused && running) { - state = AcceptorState.PAUSED; - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // Ignore + if (state != AcceptorState.PAUSED) { + pauseStart = System.nanoTime(); + // Entered pause state + state = AcceptorState.PAUSED; + } + if ((System.nanoTime() - pauseStart) > 1_000_000) { + // Paused for more than 1ms + try { + if ((System.nanoTime() - pauseStart) > 10_000_000) { + Thread.sleep(10); + } else { + Thread.sleep(1); + } + } catch (InterruptedException e) { + // Ignore + } } } diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java b/java/org/apache/tomcat/util/net/Nio2Endpoint.java index 5e0dfe1..a2e0bd3 100644 --- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java +++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java @@ -255,8 +255,8 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel> { try { long timeout = getExecutorTerminationTimeoutMillis(); while (timeout > 0 && !allClosed) { - timeout -= 100; - Thread.sleep(100); + timeout -= 1; + Thread.sleep(1); } threadGroup.shutdownNow(); if (timeout > 0) { @@ -363,17 +363,40 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel> { public void run() { int errorDelay = 0; + long pauseStart = 0; // Loop until we receive a shutdown command while (running) { - // Loop if endpoint is paused + // Loop if endpoint is paused. + // There are two likely scenarios here. + // The first scenario is that Tomcat is shutting down. In this + // case - and particularly for the unit tests - we want to exit + // this loop as quickly as possible. The second scenario is a + // genuine pause of the connector. In this case we want to avoid + // excessive CPU usage. + // Therefore, we start with a tight loop but if there isn't a + // rapid transition to stop then sleeps are introduced. + // < 1ms - tight loop + // 1ms to 10ms - 1ms sleep + // > 10ms - 10ms sleep while (paused && running) { - state = AcceptorState.PAUSED; - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // Ignore + if (state != AcceptorState.PAUSED) { + pauseStart = System.nanoTime(); + // Entered pause state + state = AcceptorState.PAUSED; + } + if ((System.nanoTime() - pauseStart) > 1_000_000) { + // Paused for more than 1ms + try { + if ((System.nanoTime() - pauseStart) > 10_000_000) { + Thread.sleep(10); + } else { + Thread.sleep(1); + } + } catch (InterruptedException e) { + // Ignore + } } } diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java index 4fd9137..8f2ee6c 100644 --- a/java/org/apache/tomcat/util/net/NioEndpoint.java +++ b/java/org/apache/tomcat/util/net/NioEndpoint.java @@ -461,18 +461,41 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> { public void run() { int errorDelay = 0; + long pauseStart = 0; // Loop until we receive a shutdown command while (running) { - // Loop if endpoint is paused - while (paused && running) { - state = AcceptorState.PAUSED; - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // Ignore - } + // Loop if endpoint is paused. + // There are two likely scenarios here. + // The first scenario is that Tomcat is shutting down. In this + // case - and particularly for the unit tests - we want to exit + // this loop as quickly as possible. The second scenario is a + // genuine pause of the connector. In this case we want to avoid + // excessive CPU usage. + // Therefore, we start with a tight loop but if there isn't a + // rapid transition to stop then sleeps are introduced. + // < 1ms - tight loop + // 1ms to 10ms - 1ms sleep + // > 10ms - 10ms sleep + while (paused && running) { + if (state != AcceptorState.PAUSED) { + pauseStart = System.nanoTime(); + // Entered pause state + state = AcceptorState.PAUSED; + } + if ((System.nanoTime() - pauseStart) > 1_000_000) { + // Paused for more than 1ms + try { + if ((System.nanoTime() - pauseStart) > 10_000_000) { + Thread.sleep(10); + } else { + Thread.sleep(1); + } + } catch (InterruptedException e) { + // Ignore + } + } } if (!running) { diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 3342f1d..0205463 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -105,6 +105,14 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 8.5.73 (schultz)"> + <subsection name="Coyote"> + <changelog> + <scode> + Improve performance of Connector shutdown - primarily to reduce the time + it takes to run the test suite. (markt) + </scode> + </changelog> + </subsection> <subsection name="WebSocket"> <changelog> <update> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org