Author: violetagg Date: Fri Aug 26 15:35:40 2016 New Revision: 1757883 URL: http://svn.apache.org/viewvc?rev=1757883&view=rev Log: Introduce a new method SocketWrapperBase.write(boolean, ByteBuffer)
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java tomcat/trunk/java/org/apache/tomcat/util/net/SocketWrapperBase.java Modified: tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java?rev=1757883&r1=1757882&r2=1757883&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java Fri Aug 26 15:35:40 2016 @@ -1154,6 +1154,41 @@ public class Nio2Endpoint extends Abstra /** + * {@inheritDoc} + * <p> + * Overridden for NIO2 to enable a gathering write to be used to write + * all of the remaining data in a single additional write should a + * non-blocking write leave data in the buffer. + */ + @Override + protected void writeNonBlocking(ByteBuffer from) throws IOException { + // Note: Possible alternate behavior: + // If there's non blocking abuse (like a test writing 1MB in a single + // "non blocking" write), then block until the previous write is + // done rather than continue buffering + // Also allows doing autoblocking + // Could be "smart" with coordination with the main CoyoteOutputStream to + // indicate the end of a write + // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) + synchronized (writeCompletionHandler) { + if (writePending.tryAcquire()) { + // No pending completion handler, so writing to the main buffer + // is possible + socketBufferHandler.configureWriteBufferForWrite(); + transfer(from, socketBufferHandler.getWriteBuffer()); + if (from.remaining() > 0) { + // Remaining data must be buffered + addToBuffers(from); + } + flushNonBlocking(true); + } else { + addToBuffers(from); + } + } + } + + + /** * @param block Ignored since this method is only called in the * blocking case */ Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SocketWrapperBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SocketWrapperBase.java?rev=1757883&r1=1757882&r2=1757883&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SocketWrapperBase.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SocketWrapperBase.java Fri Aug 26 15:35:40 2016 @@ -351,6 +351,33 @@ public abstract class SocketWrapperBase< /** + * Writes the provided data to the socket, buffering any remaining data if + * used in non-blocking mode. + * + * @param block <code>true</code> if a blocking write should be used, + * otherwise a non-blocking write will be used + * @param from The ByteBuffer containing the data to be written + * + * @throws IOException If an IO error occurs during the write + */ + public final void write(boolean block, ByteBuffer from) throws IOException { + if (from == null || from.remaining() == 0) { + return; + } + + // While the implementations for blocking and non-blocking writes are + // very similar they have been split into separate methods to allow + // sub-classes to override them individually. NIO2, for example, + // overrides the non-blocking write but not the blocking write. + if (block) { + writeBlocking(from); + } else { + writeNonBlocking(from); + } + } + + + /** * Transfers the data to the socket write buffer (writing that data to the * socket if the buffer fills up using a blocking write) until all the data * has been transferred and space remains in the socket write buffer. @@ -382,6 +409,54 @@ public abstract class SocketWrapperBase< /** + * Write the data to the socket (writing that data to the socket using a + * blocking write) until all the data has been transferred and space remains + * in the socket write buffer. If it is possible use the provided buffer + * directly and do not transfer to the socket write buffer. + * + * @param from The ByteBuffer containing the data to be written + * + * @throws IOException If an IO error occurs during the write + */ + protected void writeBlocking(ByteBuffer from) throws IOException { + // Note: There is an implementation assumption that if the switch from + // non-blocking to blocking has been made then any pending + // non-blocking writes were flushed at the time the switch + // occurred. + + // If it is possible write the data to the socket directly from the + // provided buffer otherwise transfer it to the socket write buffer + if (socketBufferHandler.isWriteBufferEmpty()) { + writeBlockingInternal(from); + } else { + socketBufferHandler.configureWriteBufferForWrite(); + transfer(from, socketBufferHandler.getWriteBuffer()); + if (!socketBufferHandler.isWriteBufferWritable()) { + doWrite(true); + writeBlockingInternal(from); + } + } + } + + + private void writeBlockingInternal(ByteBuffer from) throws IOException { + // The socket write buffer capacity is socket.appWriteBufSize + int limit = socketBufferHandler.getWriteBuffer().capacity(); + int fromLimit = from.limit(); + while (from.remaining() >= limit) { + from.limit(from.position() + limit); + doWrite(true, from); + from.limit(fromLimit); + } + + if (from.remaining() > 0) { + socketBufferHandler.configureWriteBufferForWrite(); + transfer(from, socketBufferHandler.getWriteBuffer()); + } + } + + + /** * Transfers the data to the socket write buffer (writing that data to the * socket if the buffer fills up using a non-blocking write) until either * all the data has been transferred and space remains in the socket write @@ -422,6 +497,66 @@ public abstract class SocketWrapperBase< /** + * Writes the data to the socket (writing that data to the socket using a + * non-blocking write) until either all the data has been transferred and + * space remains in the socket write buffer or a non-blocking write leaves + * data in the socket write buffer. If it is possible use the provided + * buffer directly and do not transfer to the socket write buffer. + * + * @param from The ByteBuffer containing the data to be written + * + * @throws IOException If an IO error occurs during the write + */ + protected void writeNonBlocking(ByteBuffer from) throws IOException { + if (bufferedWrites.size() == 0 && socketBufferHandler.isWriteBufferWritable()) { + if (socketBufferHandler.isWriteBufferEmpty()) { + writeNonBlockingInternal(from); + } else { + socketBufferHandler.configureWriteBufferForWrite(); + transfer(from, socketBufferHandler.getWriteBuffer()); + if (!socketBufferHandler.isWriteBufferWritable()) { + doWrite(false); + if (socketBufferHandler.isWriteBufferWritable()) { + writeNonBlockingInternal(from); + } + } + } + } + + if (from.remaining() > 0) { + // Remaining data must be buffered + addToBuffers(from); + } + } + + + private boolean writeNonBlockingInternal(ByteBuffer from) throws IOException { + // The socket write buffer capacity is socket.appWriteBufSize + int limit = socketBufferHandler.getWriteBuffer().capacity(); + int fromLimit = from.limit(); + while (from.remaining() >= limit) { + int newLimit = from.position() + limit; + from.limit(newLimit); + doWrite(false, from); + from.limit(fromLimit); + if (from.position() != newLimit) { + // Didn't write the whole amount of data in the last + // non-blocking write. + // Exit the loop. + return false; + } + } + + if (from.remaining() > 0) { + socketBufferHandler.configureWriteBufferForWrite(); + transfer(from, socketBufferHandler.getWriteBuffer()); + } + + return socketBufferHandler.isWriteBufferWritable(); + } + + + /** * Writes as much data as possible from any that remains in the buffers. * * @param block <code>true</code> if a blocking write should be used, @@ -897,15 +1032,19 @@ public abstract class SocketWrapperBase< protected static int transfer(byte[] from, int offset, int length, ByteBuffer to) { int max = Math.min(length, to.remaining()); - to.put(from, offset, max); + if (max > 0) { + to.put(from, offset, max); + } return max; } protected static void transfer(ByteBuffer from, ByteBuffer to) { int max = Math.min(from.remaining(), to.remaining()); - int fromLimit = from.limit(); - from.limit(from.position() + max); - to.put(from); - from.limit(fromLimit); + if (max > 0) { + int fromLimit = from.limit(); + from.limit(from.position() + max); + to.put(from); + from.limit(fromLimit); + } } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org