src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java
branchniosocketimpl-branch
changeset 57211 4503441bec2e
parent 57207 30695f27d7ea
child 57212 28b0946d3b81
--- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java	Sun Feb 24 07:59:46 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java	Tue Feb 26 08:53:16 2019 +0000
@@ -115,10 +115,10 @@
     private long readerThread;
     private long writerThread;
 
-    // used when SO_REUSEADDR is emulated
+    // used when SO_REUSEADDR is emulated, protected by stateLock
     private boolean isReuseAddress;
 
-    // cached value of IPV6_TCLASS or IP_TOS socket option
+    // cached value of IPV6_TCLASS or IP_TOS socket option, protected by stateLock
     private int trafficClass;
 
     // read or accept timeout in millis
@@ -128,6 +128,10 @@
     private volatile boolean isInputClosed;
     private volatile boolean isOutputClosed;
 
+    // used by read to emulate legacy behavior, protected by readLock
+    private boolean readEOF;
+    private boolean connectionReset;
+
     /**
      * Creates a instance of this SocketImpl.
      * @param server true if this is a SocketImpl for a ServerSocket
@@ -254,6 +258,7 @@
     private int timedRead(FileDescriptor fd, byte[] b, int off, int len, int millis)
         throws IOException
     {
+        assert nonBlocking;
         long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
         long remainingNanos = nanos;
         long startNanos = System.nanoTime();
@@ -273,18 +278,23 @@
 
     /**
      * Reads bytes from the socket into the given byte array.
-     * @return the number of bytes read
-     * @throws IOException if the socket is closed or an I/O occurs
+     * @return the number of bytes read or -1 at EOF
+     * @throws SocketException if the socket is closed or a socket I/O error occurs
      * @throws SocketTimeoutException if the read timeout elapses
      */
-    private int read(byte[] b, int off, int len) throws IOException {
+    private int implRead(byte[] b, int off, int len) throws IOException {
         readLock.lock();
         try {
+            // emulate legacy behavior to return -1, even if socket is closed
+            if (readEOF)
+                return -1;
             int n = 0;
             FileDescriptor fd = beginRead();
             try {
+                if (connectionReset)
+                    throw new SocketException("Connection reset");
                 if (isInputClosed)
-                    return IOStatus.EOF;
+                    return -1;
                 int timeout = this.timeout;
                 configureNonBlockingIfNeeded(fd, timeout);
                 n = tryRead(fd, b, off, len);
@@ -300,7 +310,16 @@
                         } while (IOStatus.okayToRetry(n) && isOpen());
                     }
                 }
+                if (n == -1)
+                    readEOF = true;
                 return n;
+            } catch (SocketTimeoutException e) {
+                throw e;
+            } catch (ConnectionResetException e) {
+                connectionReset = true;
+                throw new SocketException("Connection reset");
+            } catch (IOException ioe) {
+                throw new SocketException(ioe.getMessage());
             } finally {
                 endRead(n > 0);
             }
@@ -310,6 +329,24 @@
     }
 
     /**
+     * Reads bytes from the socket into the given byte array.
+     * @return the number of bytes read or -1 at EOF
+     * @throws IndexOutOfBoundsException if the bound checks fail
+     * @throws SocketException if the socket is closed or a socket I/O error occurs
+     * @throws SocketTimeoutException if the read timeout elapses
+     */
+    private int read(byte[] b, int off, int len) throws IOException {
+        Objects.checkFromIndexSize(off, len, b.length);
+        if (len == 0) {
+            return 0;
+        } else {
+            // read up to MAX_BUFFER_SIZE bytes
+            int size = Math.min(len, MAX_BUFFER_SIZE);
+            return implRead(b, off, size);
+        }
+    }
+
+    /**
      * Marks the beginning of a write operation that might block.
      * @throws SocketException if the socket is closed or not connected
      */
@@ -355,9 +392,9 @@
     /**
      * Writes a sequence of bytes to this socket from the given byte array.
      * @return the number of bytes written
-     * @throws IOException if the socket is closed or an I/O occurs
+     * @throws SocketException if the socket is closed or an socket I/O error occurs
      */
-    private int write(byte[] b, int off, int len) throws IOException {
+    private int implWrite(byte[] b, int off, int len) throws IOException {
         writeLock.lock();
         try {
             int n = 0;
@@ -369,6 +406,8 @@
                     n = tryWrite(fd, b, off, len);
                 }
                 return n;
+            } catch (IOException ioe) {
+                throw new SocketException(ioe.getMessage());
             } finally {
                 endWrite(n > 0);
             }
@@ -378,6 +417,24 @@
     }
 
     /**
+     * Writes a sequence of bytes to this socket from the given byte array.
+     * @throws SocketException if the socket is closed or an socket I/O error occurs
+     */
+    private void write(byte[] b, int off, int len) throws IOException {
+        Objects.checkFromIndexSize(off, len, b.length);
+        if (len > 0) {
+            int pos = off;
+            int end = off + len;
+            while (pos < end) {
+                // write up to MAX_BUFFER_SIZE bytes
+                int size = Math.min((end - pos), MAX_BUFFER_SIZE);
+                int n = implWrite(b, pos, size);
+                pos += n;
+            }
+        }
+    }
+
+    /**
      * Creates the socket.
      * @param stream {@code true} for a streams socket
      */
@@ -440,6 +497,7 @@
                     nsi.close();
                 } catch (IOException ignore) { }
             }
+            // copy/reset fields protected by stateLock
             synchronized (nsi.stateLock) {
                 assert nsi.state == ST_NEW || nsi.state == ST_CLOSED;
                 synchronized (this.stateLock) {
@@ -456,9 +514,9 @@
 
                     // reset fields; do not reset timeout
                     nsi.nonBlocking = false;
+                    nsi.isReuseAddress = false;
                     nsi.isInputClosed = false;
                     nsi.isOutputClosed = false;
-                    nsi.isReuseAddress = false;
                     nsi.state = ST_CONNECTED;
 
                     // GC'ing of this impl should not close the file descriptor
@@ -469,6 +527,14 @@
                     nsi.closer = FileDescriptorCloser.create(nsi);
                 }
             }
+            // reset fields protected by readLock
+            nsi.readLock.lock();
+            try {
+                nsi.readEOF = false;
+                nsi.connectionReset = false;
+            } finally {
+                nsi.readLock.unlock();
+            }
         } else {
             synchronized (this.stateLock) {
                 // this SocketImpl should be connected
@@ -696,6 +762,7 @@
                             int millis)
         throws IOException
     {
+        assert nonBlocking;
         long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
         long remainingNanos = nanos;
         long startNanos = System.nanoTime();
@@ -783,8 +850,6 @@
     @Override
     protected InputStream getInputStream() {
         return new InputStream() {
-            // EOF or connection reset detected, not thread safe
-            private boolean eof, reset;
             @Override
             public int read() throws IOException {
                 byte[] a = new byte[1];
@@ -793,31 +858,7 @@
             }
             @Override
             public int read(byte[] b, int off, int len) throws IOException {
-                Objects.checkFromIndexSize(off, len, b.length);
-                if (eof) {
-                    return -1;
-                } else if (reset) {
-                    NioSocketImpl.this.ensureOpen();
-                    throw new SocketException("Connection reset");
-                } else if (len == 0) {
-                    return 0;
-                } else {
-                    try {
-                        // read up to MAX_BUFFER_SIZE bytes
-                        int size = Math.min(len, MAX_BUFFER_SIZE);
-                        int n = NioSocketImpl.this.read(b, off, size);
-                        if (n == -1)
-                            eof = true;
-                        return n;
-                    } catch (ConnectionResetException e) {
-                        reset = true;
-                        throw new SocketException("Connection reset");
-                    } catch (SocketTimeoutException e) {
-                        throw e;
-                    } catch (IOException ioe) {
-                        throw new SocketException(ioe.getMessage());
-                    }
-                }
+                return NioSocketImpl.this.read(b, off, len);
             }
             @Override
             public int available() throws IOException {
@@ -840,23 +881,8 @@
             }
             @Override
             public void write(byte[] b, int off, int len) throws IOException {
-                Objects.checkFromIndexSize(off, len, b.length);
-                if (len > 0) {
-                    try {
-                        int pos = off;
-                        int end = off + len;
-                        while (pos < end) {
-                            // write up to MAX_BUFFER_SIZE bytes
-                            int size = Math.min((end - pos), MAX_BUFFER_SIZE);
-                            int n = NioSocketImpl.this.write(b, pos, size);
-                            pos += n;
-                        }
-                    } catch (IOException ioe) {
-                        throw new SocketException(ioe.getMessage());
-                    }
-                }
+                NioSocketImpl.this.write(b, off, len);
             }
-
             @Override
             public void close() throws IOException {
                 NioSocketImpl.this.close();