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: [email protected]
For additional commands, e-mail: [email protected]