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 081049d Align with 10.0.x/9.0.x 081049d is described below commit 081049de42cbbf7b452089ae3fedcd72852696fa Author: Mark Thomas <ma...@apache.org> AuthorDate: Thu Nov 11 16:42:07 2021 +0000 Align with 10.0.x/9.0.x --- java/org/apache/tomcat/util/net/AprEndpoint.java | 212 +++++++++++++++------ .../apache/tomcat/util/net/LocalStrings.properties | 2 + 2 files changed, 158 insertions(+), 56 deletions(-) diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java index 3583a2d..f369000 100644 --- a/java/org/apache/tomcat/util/net/AprEndpoint.java +++ b/java/org/apache/tomcat/util/net/AprEndpoint.java @@ -72,8 +72,6 @@ import org.apache.tomcat.util.net.openssl.OpenSSLUtil; * <li>Worker threads pool</li> * </ul> * - * TODO: Consider using the virtual machine's thread pool. - * * @author Mladen Turk * @author Remy Maucherat */ @@ -298,6 +296,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { // Create the pool for the server socket serverSockPool = Pool.create(rootPool); + // Create the APR address that will be bound String addressStr = null; if (getAddress() != null) { @@ -322,6 +321,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { Socket.optSet(serverSock, Socket.APR_IPV6_V6ONLY, 0); } } + // Deal with the firewalls that tend to drop the inactive sockets Socket.optSet(serverSock, Socket.APR_SO_KEEPALIVE, 1); // Bind the server socket @@ -329,11 +329,13 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { if (ret != 0) { throw new Exception(sm.getString("endpoint.init.bind", "" + ret, Error.strerror(ret))); } + // Start listening on the server socket ret = Socket.listen(serverSock, getAcceptCount()); if (ret != 0) { throw new Exception(sm.getString("endpoint.init.listen", "" + ret, Error.strerror(ret))); } + if (OS.IS_WIN32 || OS.IS_WIN64) { // On Windows set the reuseaddr flag after the bind/listen Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1); @@ -465,20 +467,13 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { // Start poller thread poller = new Poller(); poller.init(); - Thread pollerThread = new Thread(poller, getName() + "-Poller"); - pollerThread.setPriority(threadPriority); - pollerThread.setDaemon(true); - pollerThread.start(); + poller.start(); // Start sendfile thread if (getUseSendfile()) { sendfile = new Sendfile(); sendfile.init(); - Thread sendfileThread = - new Thread(sendfile, getName() + "-Sendfile"); - sendfileThread.setPriority(threadPriority); - sendfileThread.setDaemon(true); - sendfileThread.start(); + sendfile.start(); } startAcceptorThreads(); @@ -497,14 +492,11 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { } if (running) { running = false; + + // Stop the Poller calling select poller.stop(); - for (SocketWrapperBase<Long> socketWrapper : connections.values()) { - try { - socketWrapper.close(); - } catch (IOException e) { - // Ignore - } - } + + // Wait for the acceptor(s) to shutdown for (AbstractEndpoint.Acceptor acceptor : acceptors) { long waitLeft = 10000; while (waitLeft > 0 && @@ -528,12 +520,42 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { } } } - // Close any sockets not in the poller performing blocking - // read/writes. Need to do this before destroying the poller since - // that will also destroy the root pool for these sockets. - for (Long s : connections.keySet()) { - Socket.shutdown(s.longValue(), Socket.APR_SHUTDOWN_READWRITE); + + // Wait for Poller to stop + int waitMillis = 0; + try { + while (poller.pollerThread.isAlive() && waitMillis < 10000) { + waitMillis++; + Thread.sleep(1); + } + } catch (InterruptedException e) { + // Ignore } + + // Close the SocketWrapper for each open connection - this should + // trigger a IOException when the app (or container) tries to write. + // Use the blocking status write lock as a proxy for a lock on + // writing to the socket. Don't want to close it while another + // thread is writing as that could trigger a JVM crash. + for (SocketWrapperBase<Long> socketWrapper : connections.values()) { + WriteLock wl = ((AprSocketWrapper) socketWrapper).getBlockingStatusWriteLock(); + wl.lock(); + try { + socketWrapper.close(); + } catch (IOException e) { + // Ignore + } finally { + wl.unlock(); + } + } + + for (Long socket : connections.keySet()) { + // Close the APR Socket. Need to do this before destroying the + // poller since that will also destroy the root pool for these + // sockets. + Socket.shutdown(socket.longValue(), Socket.APR_SHUTDOWN_READWRITE); + } + try { poller.destroy(); } catch (Exception e) { @@ -543,6 +565,25 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { connections.clear(); if (getUseSendfile()) { try { + sendfile.stop(); + + // Wait for the sendfile thread to exit, otherwise parallel + // destruction of sockets which are still in the poller can cause + // problems. + waitMillis = 0; + try { + while (sendfile.sendfileThread.isAlive() && waitMillis < 10000) { + waitMillis++; + Thread.sleep(1); + } + } catch (InterruptedException e) { + // Ignore + } + + if (sendfile.sendfileThread.isAlive()) { + log.warn(sm.getString("endpoint.sendfileThreadStop")); + } + sendfile.destroy(); } catch (Exception e) { // Ignore @@ -1154,7 +1195,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { private AtomicInteger connectionCount = new AtomicInteger(0); public int getConnectionCount() { return connectionCount.get(); } - + private volatile Thread pollerThread; private volatile boolean pollerRunning = true; /** @@ -1187,12 +1228,22 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { } + protected void start() { + pollerThread = new Thread(poller, getName() + "-Poller"); + pollerThread.setPriority(threadPriority); + pollerThread.setDaemon(true); + pollerThread.start(); + } + + /* * This method is synchronized so that it is not possible for a socket * to be added to the Poller's addList once this method has completed. */ protected synchronized void stop() { pollerRunning = false; + // In case the poller thread is in the idle wait + this.notify(); } @@ -1200,21 +1251,27 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { * Destroy the poller. */ protected synchronized void destroy() { - // Wait for pollerTime before doing anything, so that the poller - // threads exit, otherwise parallel destruction of sockets which are - // still in the poller can cause problems - try { - this.notify(); - this.wait(pollTime / 1000); - } catch (InterruptedException e) { - // Ignore + // Wait for the poller thread to exit, otherwise parallel + // destruction of sockets which are still in the poller can cause + // problems. + int loops = 50; + while (loops > 0 && pollerThread.isAlive()) { + try { + this.wait(pollTime / 1000); + } catch (InterruptedException e) { + // Ignore + } + loops--; + } + if (pollerThread.isAlive()) { + log.warn(sm.getString("endpoint.pollerThreadStop")); } // Close all sockets in the close queue SocketInfo info = closeList.get(); while (info != null) { // Make sure we aren't trying add the socket as well as close it addList.remove(info.socket); - // Make sure the socket isn't in the poller before we close it + // Make sure the socket isn't in the poller before we close it removeFromPoller(info.socket); // Poller isn't running at this point so use destroySocket() // directly @@ -1225,7 +1282,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { // Close all sockets in the add queue info = addList.get(); while (info != null) { - // Make sure the socket isn't in the poller before we close it + // Make sure the socket isn't in the poller before we close it removeFromPoller(info.socket); // Poller isn't running at this point so use destroySocket() // directly @@ -1351,8 +1408,10 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { Long.valueOf(socket))); } SocketWrapperBase<Long> socketWrapper = connections.get(Long.valueOf(socket)); - socketWrapper.setError(new SocketTimeoutException()); - processSocket(socketWrapper, SocketEvent.ERROR, true); + if (socketWrapper != null) { + socketWrapper.setError(new SocketTimeoutException()); + processSocket(socketWrapper, SocketEvent.ERROR, true); + } socket = timeouts.check(date); } @@ -1403,7 +1462,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { // with no processing since the notify() call in // add()/close() would have no effect since it // happened before this sync block was entered - if (addList.size() < 1 && closeList.size() < 1) { + if (pollerRunning && addList.size() < 1 && closeList.size() < 1) { this.wait(10000); } } @@ -1726,6 +1785,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { protected ArrayList<SendfileData> addS; + private volatile Thread sendfileThread; private volatile boolean sendfileRunning = true; /** @@ -1743,22 +1803,23 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { addS = new ArrayList<>(); } + protected void start() { + sendfileThread = new Thread(sendfile, getName() + "-Sendfile"); + sendfileThread.setPriority(threadPriority); + sendfileThread.setDaemon(true); + sendfileThread.start(); + } + + protected synchronized void stop() { + sendfileRunning = false; + // In case the sendfile thread is in the idle wait + this.notify(); + } + /** * Destroy the poller. */ protected void destroy() { - sendfileRunning = false; - // Wait for polltime before doing anything, so that the poller threads - // exit, otherwise parallel destruction of sockets which are still - // in the poller can cause problems - try { - synchronized (this) { - this.notify(); - this.wait(pollTime / 1000); - } - } catch (InterruptedException e) { - // Ignore - } // Close any socket remaining in the add queue for (int i = (addS.size() - 1); i >= 0; i--) { SendfileData data = addS.get(i); @@ -1859,7 +1920,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { // Loop if endpoint is paused while (sendfileRunning && paused) { try { - Thread.sleep(1000); + Thread.sleep(pollTime / 1000); } catch (InterruptedException e) { // Ignore } @@ -2065,8 +2126,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { return; } // Process the request from this socket - Handler.SocketState state = getHandler().process(socket, - SocketEvent.OPEN_READ); + Handler.SocketState state = getHandler().process(socket, SocketEvent.OPEN_READ); if (state == Handler.SocketState.CLOSED) { // Close socket and pool closeSocket(socket.getSocket().longValue()); @@ -2085,7 +2145,7 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { * This class is the equivalent of the Worker, but will simply use in an * external Executor thread pool. */ - protected class SocketProcessor extends SocketProcessorBase<Long> { + protected class SocketProcessor extends SocketProcessorBase<Long> { public SocketProcessor(SocketWrapperBase<Long> socketWrapper, SocketEvent event) { super(socketWrapper, event); @@ -2633,10 +2693,50 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack { @Override public void doClientAuth(SSLSupport sslSupport) throws IOException { long socket = getSocket().longValue(); - // Configure connection to require a certificate + // Configure connection to require a certificate. This requires a + // re-handshake and must block until the re-handshake completes. + // Therefore, make sure socket is in blocking mode. + Lock readLock = getBlockingStatusReadLock(); + WriteLock writeLock = getBlockingStatusWriteLock(); + boolean renegotiateDone = false; try { - SSLSocket.setVerify(socket, SSL.SSL_CVERIFY_REQUIRE, -1); - SSLSocket.renegotiate(socket); + readLock.lock(); + try { + if (getBlockingStatus()) { + Socket.timeoutSet(getSocket().longValue(), getReadTimeout() * 1000); + + SSLSocket.setVerify(socket, SSL.SSL_CVERIFY_REQUIRE, -1); + SSLSocket.renegotiate(socket); + + renegotiateDone = true; + } + } finally { + readLock.unlock(); + } + + if (!renegotiateDone) { + writeLock.lock(); + try { + // Set the current settings for this socket + setBlockingStatus(true); + Socket.timeoutSet(getSocket().longValue(), getReadTimeout() * 1000); + // Downgrade the lock + readLock.lock(); + try { + writeLock.unlock(); + SSLSocket.setVerify(socket, SSL.SSL_CVERIFY_REQUIRE, -1); + SSLSocket.renegotiate(socket); + } finally { + readLock.unlock(); + } + } finally { + // Should have been released above but may not have been on some + // exception paths + if (writeLock.isHeldByCurrentThread()) { + writeLock.unlock(); + } + } + } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); throw new IOException(sm.getString("socket.sslreneg"), t); diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties index eeeeede..dccd14b 100644 --- a/java/org/apache/tomcat/util/net/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/LocalStrings.properties @@ -105,11 +105,13 @@ endpoint.poll.error=Unexpected poller error endpoint.poll.fail=Critical poller failure (restarting poller): [{0}] [{1}] endpoint.poll.initfail=Poller creation failed endpoint.poll.limitedpollsize=Failed to create poller with specified size of [{0}] +endpoint.pollerThreadStop=The poller thread failed to stop in a timely manner endpoint.process.fail=Error allocating socket processor endpoint.processing.fail=Error running socket processor endpoint.removeDefaultSslHostConfig=The default SSLHostConfig (named [{0}]) may not be removed endpoint.sendfile.addfail=Sendfile failure: [{0}] [{1}] endpoint.sendfile.error=Unexpected sendfile error +endpoint.sendfileThreadStop=The sendfile thread failed to stop in a timely manner endpoint.serverSocket.closeFailed=Failed to close server socket for [{0}] endpoint.setAttribute=Set [{0}] to [{1}] endpoint.timeout.err=Error processing socket timeout --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org