You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/10/11 15:50:10 UTC

[tomcat] branch 8.5.x updated: Improve Connector stop performance - primarily to speed up tests

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org