Author: markt Date: Tue May 20 19:02:46 2014 New Revision: 1596367 URL: http://svn.apache.org/r1596367 Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56521 Implement a TODO Reuse the async write buffer between writes to reduce allocation and GC overhead. Based on a patch by leonzhx. Also: - don't re-allocate the buffer / move the data in the buffer if the buffer is partially written on a subsequent write - make the buffer size configurable, defaulting to 8k
Modified: tomcat/tc7.0.x/trunk/ (props changed) tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprProcessor.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioProcessor.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioProcessor.java tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml Propchange: tomcat/tc7.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1596275 Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java Tue May 20 19:02:46 2014 @@ -174,6 +174,17 @@ public abstract class AbstractHttp11Prot } + /** + * The size of the buffer used by the ServletOutputStream when performing + * delayed asynchronous writes using HTTP upgraded connections. + */ + private int upgradeAsyncWriteBufferSize = 8192; + public int getUpgradeAsyncWriteBufferSize() { return upgradeAsyncWriteBufferSize; } + public void setUpgradeAsyncWriteBufferSize(int upgradeAsyncWriteBufferSize) { + this.upgradeAsyncWriteBufferSize = upgradeAsyncWriteBufferSize; + } + + // ------------------------------------------------ HTTP specific properties // ------------------------------------------ passed through to the EndPoint Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java Tue May 20 19:02:46 2014 @@ -339,7 +339,8 @@ public class Http11AprProtocol extends A HttpUpgradeHandler httpUpgradeProcessor) throws IOException { return new AprProcessor(socket, httpUpgradeProcessor, - (AprEndpoint) proto.endpoint); + (AprEndpoint) proto.endpoint, + proto.getUpgradeAsyncWriteBufferSize()); } } } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java Tue May 20 19:02:46 2014 @@ -298,7 +298,8 @@ public class Http11NioProtocol extends A HttpUpgradeHandler httpUpgradeProcessor) throws IOException { return new NioProcessor(socket, httpUpgradeProcessor, - ((Http11NioProtocol) getProtocol()).getEndpoint().getSelectorPool()); + proto.getEndpoint().getSelectorPool(), + proto.getUpgradeAsyncWriteBufferSize()); } } } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java Tue May 20 19:02:46 2014 @@ -203,7 +203,8 @@ public class Http11Protocol extends Abst SocketWrapper<Socket> socket, HttpUpgradeHandler httpUpgradeProcessor) throws IOException { - return new BioProcessor(socket, httpUpgradeProcessor); + return new BioProcessor(socket, httpUpgradeProcessor, + proto.getUpgradeAsyncWriteBufferSize()); } } } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AbstractServletOutputStream.java Tue May 20 19:02:46 2014 @@ -56,6 +56,16 @@ public abstract class AbstractServletOut // Writes guarded by writeLock private volatile byte[] buffer; + private volatile int bufferPos; + private volatile int bufferLimit; + private final int asyncWriteBufferSize; + + + public AbstractServletOutputStream(int asyncWriteBufferSize) { + this.asyncWriteBufferSize = asyncWriteBufferSize; + buffer = new byte[asyncWriteBufferSize]; + } + /** * New Servlet 3.1 method. @@ -69,7 +79,7 @@ public abstract class AbstractServletOut // Make sure isReady() and onWritePossible() have a consistent view of // buffer and fireListener when determining if the listener should fire synchronized (fireListenerLock) { - boolean result = (buffer == null); + boolean result = (bufferLimit == 0); fireListener = !result; return result; } @@ -120,9 +130,8 @@ public abstract class AbstractServletOut } private void preWriteChecks() { - if (buffer != null) { - throw new IllegalStateException( - sm.getString("upgrade.sis.write.ise")); + if (bufferLimit != 0) { + throw new IllegalStateException(sm.getString("upgrade.sis.write.ise")); } } @@ -143,12 +152,25 @@ public abstract class AbstractServletOut // write executes. int written = doWrite(false, b, off, len); if (written < len) { - // TODO: - Reuse the buffer - // - Only reallocate if it gets too big (>8k?) - buffer = new byte[len - written]; - System.arraycopy(b, off + written, buffer, 0, len - written); + if (b == buffer) { + // This is a partial write of the existing buffer. Just + // increment the current position + bufferPos += written; + } else { + // This is a new partial write + int bytesLeft = len - written; + if (bytesLeft > buffer.length) { + buffer = new byte[bytesLeft]; + } else if (bytesLeft < asyncWriteBufferSize && + buffer.length > asyncWriteBufferSize) { + buffer = new byte[asyncWriteBufferSize]; + } + bufferPos = 0; + bufferLimit = bytesLeft; + System.arraycopy(b, off + written, buffer, bufferPos, bufferLimit); + } } else { - buffer = null; + bufferLimit = 0; } } } @@ -157,8 +179,8 @@ public abstract class AbstractServletOut protected final void onWritePossible() throws IOException { try { synchronized (writeLock) { - if (buffer != null) { - writeInternal(buffer, 0, buffer.length); + if (bufferLimit > 0) { + writeInternal(buffer, bufferPos, bufferLimit - bufferPos); } } } catch (Throwable t) { @@ -176,7 +198,7 @@ public abstract class AbstractServletOut boolean fire = false; synchronized (fireListenerLock) { - if (buffer == null && fireListener) { + if (bufferLimit == 0 && fireListener) { fireListener = false; fire = true; } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprProcessor.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprProcessor.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprProcessor.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprProcessor.java Tue May 20 19:02:46 2014 @@ -32,10 +32,11 @@ public class AprProcessor extends Abstra private static final int INFINITE_TIMEOUT = -1; public AprProcessor(SocketWrapper<Long> wrapper, - HttpUpgradeHandler httpUpgradeProcessor, AprEndpoint endpoint) { + HttpUpgradeHandler httpUpgradeProcessor, AprEndpoint endpoint, + int asyncWriteBufferSize) { super(httpUpgradeProcessor, new AprServletInputStream(wrapper), - new AprServletOutputStream(wrapper, endpoint)); + new AprServletOutputStream(wrapper, asyncWriteBufferSize, endpoint)); Socket.timeoutSet(wrapper.getSocket().longValue(), INFINITE_TIMEOUT); } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/AprServletOutputStream.java Tue May 20 19:02:46 2014 @@ -39,7 +39,8 @@ public class AprServletOutputStream exte private final ByteBuffer sslOutputBuffer; public AprServletOutputStream(SocketWrapper<Long> wrapper, - AprEndpoint endpoint) { + int asyncWriteBufferSize, AprEndpoint endpoint) { + super(asyncWriteBufferSize); this.endpoint = endpoint; this.wrapper = wrapper; this.socket = wrapper.getSocket().longValue(); Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioProcessor.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioProcessor.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioProcessor.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioProcessor.java Tue May 20 19:02:46 2014 @@ -33,9 +33,10 @@ public class BioProcessor extends Abstra private static final int INFINITE_TIMEOUT = 0; public BioProcessor(SocketWrapper<Socket> wrapper, - HttpUpgradeHandler httpUpgradeProcessor) throws IOException { + HttpUpgradeHandler httpUpgradeProcessor, + int asyncWriteBufferSize) throws IOException { super(httpUpgradeProcessor, new BioServletInputStream(wrapper), - new BioServletOutputStream(wrapper)); + new BioServletOutputStream(wrapper, asyncWriteBufferSize)); wrapper.getSocket().setSoTimeout(INFINITE_TIMEOUT); } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/BioServletOutputStream.java Tue May 20 19:02:46 2014 @@ -26,9 +26,10 @@ public class BioServletOutputStream exte private final OutputStream os; - public BioServletOutputStream(SocketWrapper<Socket> wrapper) - throws IOException { - os = wrapper.getSocket().getOutputStream(); + public BioServletOutputStream(SocketWrapper<Socket> socketWrapper, + int asyncWriteBufferSize) throws IOException { + super(asyncWriteBufferSize); + os = socketWrapper.getSocket().getOutputStream(); } @Override Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioProcessor.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioProcessor.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioProcessor.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioProcessor.java Tue May 20 19:02:46 2014 @@ -32,10 +32,11 @@ public class NioProcessor extends Abstra private static final int INFINITE_TIMEOUT = -1; public NioProcessor(SocketWrapper<NioChannel> wrapper, - HttpUpgradeHandler httpUpgradeProcessor, NioSelectorPool pool) { + HttpUpgradeHandler httpUpgradeProcessor, NioSelectorPool pool, + int asyncWriteBufferSize) { super(httpUpgradeProcessor, new NioServletInputStream(wrapper, pool), - new NioServletOutputStream(wrapper, pool)); + new NioServletOutputStream(wrapper, asyncWriteBufferSize, pool)); wrapper.setTimeout(INFINITE_TIMEOUT); } Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/NioServletOutputStream.java Tue May 20 19:02:46 2014 @@ -32,9 +32,10 @@ public class NioServletOutputStream exte private final int maxWrite; - public NioServletOutputStream( - SocketWrapper<NioChannel> wrapper, NioSelectorPool pool) { - channel = wrapper.getSocket(); + public NioServletOutputStream(SocketWrapper<NioChannel> socketWrapper, + int asyncWriteBufferSize, NioSelectorPool pool) { + super(asyncWriteBufferSize); + channel = socketWrapper.getSocket(); this.pool = pool; maxWrite = channel.getBufHandler().getWriteBuffer().capacity(); } Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Tue May 20 19:02:46 2014 @@ -59,15 +59,26 @@ <subsection name="Catalina"> <changelog> <add> - <bug>56526</bug>: Improved the <code>StuckThreadDetectionValve</code> to - optionally interrupt stuck threads to attempt to unblock them. - (slaurent) - </add> - <add> <bug>56461</bug>: New <code>failCtxIfServletStartFails</code> attribute on Context and Host configuration to force the context startup to fail if a load-on-startup servlet fails its startup. (slaurent) </add> + <add> + <bug>56526</bug>: Improved the <code>StuckThreadDetectionValve</code> to + optionally interrupt stuck threads to attempt to unblock them. + (slaurent) + </add> + </changelog> + </subsection> + <subsection name="Coyote"> + <changelog> + <fix> + <bug>56521</bug>: Re-use the asynchronous write buffer between writes to + reduce allocation and GC overhead. Based on a patch by leonzhx. Also + make the buffer size configurable and remove copying of data within + buffer when the buffer is only partially written on a subsequent write. + (markt) + </fix> </changelog> </subsection> </section> Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml?rev=1596367&r1=1596366&r2=1596367&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml Tue May 20 19:02:46 2014 @@ -523,6 +523,14 @@ </p> </attribute> + <attribute name="upgradeAsyncWriteBufferSize" required="false"> + <p>The default size of the buffer to allocate to for asynchronous writes + that can not be completed in a single operation. Data that can't be + written immediately will be stored in this buffer until it can be written. + If more data needs to be stored than space is available in the buffer than + the size of the buffer will be increased for the duration of the write. If + not specified the default value of 8192 will be used.</p> + </attribute> </attributes> </subsection> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org