Author: markt Date: Tue May 13 22:51:19 2014 New Revision: 1594411 URL: http://svn.apache.org/r1594411 Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56518 Refactor NIO's SocketProcessor to use KeyAtrtachment rather than Socket to align it with the other implementations. This enables a number of attachment->socket->attachment transitions to be skipped which appears to resolve the root cause of BZ 56518. Since the wrapper is available and can be processed, it becomes possible to reduce the connection count when the socket is closed by the interrupt.
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java?rev=1594411&r1=1594410&r2=1594411&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java Tue May 13 22:51:19 2014 @@ -605,23 +605,22 @@ public class NioEndpoint extends Abstrac public void processSocket(SocketWrapper<NioChannel> socketWrapper, SocketStatus socketStatus, boolean dispatch) { NioChannel socket = socketWrapper.getSocket(); - if (dispatch && socketStatus == SocketStatus.OPEN_READ) { + if (socket.isOpen() && dispatch && socketStatus == SocketStatus.OPEN_READ) { socket.getPoller().add(socket, OP_CALLBACK); } else { - processSocket(socket, socketStatus, dispatch); + processSocket((KeyAttachment) socketWrapper, socketStatus, dispatch); } } - protected boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) { + protected boolean processSocket(KeyAttachment attachment, SocketStatus status, boolean dispatch) { try { - KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false); if (attachment == null) { return false; } attachment.setCometNotify(false); //will get reset upon next reg SocketProcessor sc = processorCache.pop(); - if ( sc == null ) sc = new SocketProcessor(socket,status); - else sc.reset(socket,status); + if ( sc == null ) sc = new SocketProcessor(attachment, status); + else sc.reset(attachment, status); Executor executor = getExecutor(); if (dispatch && executor != null) { executor.execute(sc); @@ -629,7 +628,7 @@ public class NioEndpoint extends Abstrac sc.run(); } } catch (RejectedExecutionException ree) { - log.warn(sm.getString("endpoint.executor.fail", socket), ree); + log.warn(sm.getString("endpoint.executor.fail", attachment.getSocket()), ree); return false; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); @@ -900,7 +899,8 @@ public class NioEndpoint extends Abstrac } addEvent(r); if (close) { - processSocket(socket, SocketStatus.STOP, false); + NioEndpoint.KeyAttachment ka = (NioEndpoint.KeyAttachment)socket.getAttachment(false); + processSocket(ka, SocketStatus.STOP, false); } } @@ -956,12 +956,12 @@ public class NioEndpoint extends Abstrac if (ka != null && ka.isComet() && status != null) { ka.setComet(false);//to avoid a loop if (status == SocketStatus.TIMEOUT ) { - if (processSocket(ka.getSocket(), status, true)) { + if (processSocket(ka, status, true)) { return; // don't close on comet timeout } } else { // Don't dispatch if the lines below are canceling the key - processSocket(ka.getSocket(), status, false); + processSocket(ka, status, false); } } key.attach(null); @@ -1126,7 +1126,6 @@ public class NioEndpoint extends Abstrac cancelledKey(sk, SocketStatus.STOP); } else if ( sk.isValid() && attachment != null ) { attachment.access();//make sure we don't time out valid sockets - NioChannel channel = attachment.getSocket(); if (sk.isReadable() || sk.isWritable() ) { if ( attachment.getSendfileData() != null ) { processSendfile(sk,attachment, false); @@ -1136,12 +1135,12 @@ public class NioEndpoint extends Abstrac boolean closeSocket = false; // Read goes before write if (sk.isReadable()) { - if (!processSocket(channel, SocketStatus.OPEN_READ, true)) { + if (!processSocket(attachment, SocketStatus.OPEN_READ, true)) { closeSocket = true; } } if (!closeSocket && sk.isWritable()) { - if (!processSocket(channel, SocketStatus.OPEN_WRITE, true)) { + if (!processSocket(attachment, SocketStatus.OPEN_WRITE, true)) { closeSocket = true; } } @@ -1302,7 +1301,7 @@ public class NioEndpoint extends Abstrac int ops = ka.interestOps() & ~OP_CALLBACK; reg(key,ka,0);//avoid multiple calls, this gets re-registered after invocation ka.interestOps(ops); - if (!processSocket(ka.getSocket(), SocketStatus.OPEN_READ, true)) processSocket(ka.getSocket(), SocketStatus.DISCONNECT, true); + if (!processSocket(ka, SocketStatus.OPEN_READ, true)) processSocket(ka, SocketStatus.DISCONNECT, true); } else if ((ka.interestOps()&SelectionKey.OP_READ) == SelectionKey.OP_READ || (ka.interestOps()&SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { //only timeout sockets that we are waiting for a read from @@ -1331,7 +1330,7 @@ public class NioEndpoint extends Abstrac if (isTimedout) { // Prevent subsequent timeouts if the timeout event takes a while to process ka.access(Long.MAX_VALUE); - processSocket(ka.getSocket(), SocketStatus.TIMEOUT, true); + processSocket(ka, SocketStatus.TIMEOUT, true); } } }//end if @@ -1493,27 +1492,23 @@ public class NioEndpoint extends Abstrac */ protected class SocketProcessor implements Runnable { - private NioChannel socket = null; + private KeyAttachment ka = null; private SocketStatus status = null; - public SocketProcessor(NioChannel socket, SocketStatus status) { - reset(socket,status); + public SocketProcessor(KeyAttachment ka, SocketStatus status) { + reset(ka, status); } - public void reset(NioChannel socket, SocketStatus status) { - this.socket = socket; + public void reset(KeyAttachment ka, SocketStatus status) { + this.ka = ka; this.status = status; } @Override public void run() { + NioChannel socket = ka.getSocket(); SelectionKey key = socket.getIOChannel().keyFor( socket.getPoller().getSelector()); - KeyAttachment ka = null; - - if (key != null) { - ka = (KeyAttachment)key.attachment(); - } // Upgraded connections need to allow multiple threads to access the // connection at the same time to enable blocking IO to be used when @@ -1531,6 +1526,8 @@ public class NioEndpoint extends Abstrac } private void doRun(SelectionKey key, KeyAttachment ka) { + NioChannel socket = ka.getSocket(); + try { int handshake = -1; @@ -1571,20 +1568,20 @@ public class NioEndpoint extends Abstrac if (state == SocketState.CLOSED) { // Close socket and pool try { - if (ka!=null) ka.setComet(false); + ka.setComet(false); socket.getPoller().cancelledKey(key, SocketStatus.ERROR); if (running && !paused) { nioChannels.push(socket); } socket = null; - if (running && !paused && ka != null) { + if (running && !paused) { keyCache.push(ka); } ka = null; } catch (Exception x) { log.error("",x); } - } else if (state == SocketState.LONG && ka != null && ka.isAsync() && ka.interestOps() > 0) { + } else if (state == SocketState.LONG && ka.isAsync() && ka.interestOps() > 0) { //we are async, and we are interested in operations ka.getPoller().add(socket, ka.interestOps()); } @@ -1596,7 +1593,7 @@ public class NioEndpoint extends Abstrac nioChannels.push(socket); } socket = null; - if (running && !paused && ka != null) { + if (running && !paused) { keyCache.push(ka); } ka = null; @@ -1604,7 +1601,9 @@ public class NioEndpoint extends Abstrac ka.getPoller().add(socket,handshake); } } catch (CancelledKeyException cx) { - socket.getPoller().cancelledKey(key, null); + if (socket != null) { + socket.getPoller().cancelledKey(key, null); + } } catch (OutOfMemoryError oom) { try { oomParachuteData = null; Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1594411&r1=1594410&r2=1594411&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Tue May 13 22:51:19 2014 @@ -232,6 +232,10 @@ <fix> Fix possible corruption if doing keepalive after a comet request. (remm) </fix> + <fix> + <bug>56518</bug>: Fix connection limit latch leak when a non-container + thread is interrupted during asynchronous processing. (markt) + </fix> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org