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

Reply via email to