src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
branchunixdomainchannels
changeset 58801 119ac9128c1b
parent 55081 dd321e3596c0
child 58847 692de65ab293
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Fri Oct 25 14:50:16 2019 +0100
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Fri Oct 25 15:56:35 2019 +0100
@@ -64,7 +64,7 @@
  * An implementation of SocketChannels
  */
 
-class SocketChannelImpl
+public abstract class SocketChannelImpl
     extends SocketChannel
     implements SelChImpl
 {
@@ -72,87 +72,64 @@
     private static final NativeDispatcher nd = new SocketDispatcher();
 
     // Our file descriptor object
-    private final FileDescriptor fd;
+    final FileDescriptor fd;
     private final int fdVal;
 
     // Lock held by current reading or connecting thread
-    private final ReentrantLock readLock = new ReentrantLock();
+    final ReentrantLock readLock = new ReentrantLock();
 
     // Lock held by current writing or connecting thread
-    private final ReentrantLock writeLock = new ReentrantLock();
+    final ReentrantLock writeLock = new ReentrantLock();
 
     // Lock held by any thread that modifies the state fields declared below
     // DO NOT invoke a blocking I/O operation while holding this lock!
-    private final Object stateLock = new Object();
+    final Object stateLock = new Object();
 
     // Input/Output closed
-    private volatile boolean isInputClosed;
-    private volatile boolean isOutputClosed;
+    volatile boolean isInputClosed;
+    volatile boolean isOutputClosed;
 
     // Connection reset protected by readLock
     private boolean connectionReset;
 
     // -- The following fields are protected by stateLock
 
-    // set true when exclusive binding is on and SO_REUSEADDR is emulated
-    private boolean isReuseAddress;
-
     // State, increases monotonically
-    private static final int ST_UNCONNECTED = 0;
-    private static final int ST_CONNECTIONPENDING = 1;
-    private static final int ST_CONNECTED = 2;
-    private static final int ST_CLOSING = 3;
-    private static final int ST_CLOSED = 4;
-    private volatile int state;  // need stateLock to change
+    static final int ST_UNCONNECTED = 0;
+    static final int ST_CONNECTIONPENDING = 1;
+    static final int ST_CONNECTED = 2;
+    static final int ST_CLOSING = 3;
+    static final int ST_CLOSED = 4;
+    volatile int state;  // need stateLock to change
 
     // IDs of native threads doing reads and writes, for signalling
     private long readerThread;
     private long writerThread;
 
     // Binding
-    private InetSocketAddress localAddress;
-    private InetSocketAddress remoteAddress;
+    SocketAddress localAddress;
+    SocketAddress remoteAddress;
 
     // Socket adaptor, created on demand
-    private Socket socket;
+    Socket socket;
 
     // -- End of fields protected by stateLock
 
 
     // Constructor for normal connecting sockets
     //
-    SocketChannelImpl(SelectorProvider sp) throws IOException {
+    public SocketChannelImpl(SelectorProvider sp) throws IOException {
         super(sp);
         this.fd = Net.socket(true);
         this.fdVal = IOUtil.fdVal(fd);
     }
 
-    SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound)
+    public SocketChannelImpl(SelectorProvider sp, FileDescriptor fd)
         throws IOException
     {
         super(sp);
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
-        if (bound) {
-            synchronized (stateLock) {
-                this.localAddress = Net.localAddress(fd);
-            }
-        }
-    }
-
-    // Constructor for sockets obtained from server sockets
-    //
-    SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa)
-        throws IOException
-    {
-        super(sp);
-        this.fd = fd;
-        this.fdVal = IOUtil.fdVal(fd);
-        synchronized (stateLock) {
-            this.localAddress = Net.localAddress(fd);
-            this.remoteAddress = isa;
-            this.state = ST_CONNECTED;
-        }
     }
 
     /**
@@ -160,7 +137,7 @@
      *
      * @throws ClosedChannelException if channel is closed (or closing)
      */
-    private void ensureOpen() throws ClosedChannelException {
+    void ensureOpen() throws ClosedChannelException {
         if (!isOpen())
             throw new ClosedChannelException();
     }
@@ -186,119 +163,6 @@
         }
     }
 
-    @Override
-    public Socket socket() {
-        synchronized (stateLock) {
-            if (socket == null)
-                socket = SocketAdaptor.create(this);
-            return socket;
-        }
-    }
-
-    @Override
-    public SocketAddress getLocalAddress() throws IOException {
-        synchronized (stateLock) {
-            ensureOpen();
-            return Net.getRevealedLocalAddress(localAddress);
-        }
-    }
-
-    @Override
-    public SocketAddress getRemoteAddress() throws IOException {
-        synchronized (stateLock) {
-            ensureOpen();
-            return remoteAddress;
-        }
-    }
-
-    @Override
-    public <T> SocketChannel setOption(SocketOption<T> name, T value)
-        throws IOException
-    {
-        Objects.requireNonNull(name);
-        if (!supportedOptions().contains(name))
-            throw new UnsupportedOperationException("'" + name + "' not supported");
-        if (!name.type().isInstance(value))
-            throw new IllegalArgumentException("Invalid value '" + value + "'");
-
-        synchronized (stateLock) {
-            ensureOpen();
-
-            if (name == StandardSocketOptions.IP_TOS) {
-                ProtocolFamily family = Net.isIPv6Available() ?
-                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
-                Net.setSocketOption(fd, family, name, value);
-                return this;
-            }
-
-            if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
-                // SO_REUSEADDR emulated when using exclusive bind
-                isReuseAddress = (Boolean)value;
-                return this;
-            }
-
-            // no options that require special handling
-            Net.setSocketOption(fd, name, value);
-            return this;
-        }
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> T getOption(SocketOption<T> name)
-        throws IOException
-    {
-        Objects.requireNonNull(name);
-        if (!supportedOptions().contains(name))
-            throw new UnsupportedOperationException("'" + name + "' not supported");
-
-        synchronized (stateLock) {
-            ensureOpen();
-
-            if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
-                // SO_REUSEADDR emulated when using exclusive bind
-                return (T)Boolean.valueOf(isReuseAddress);
-            }
-
-            // special handling for IP_TOS: always return 0 when IPv6
-            if (name == StandardSocketOptions.IP_TOS) {
-                ProtocolFamily family = Net.isIPv6Available() ?
-                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
-                return (T) Net.getSocketOption(fd, family, name);
-            }
-
-            // no options that require special handling
-            return (T) Net.getSocketOption(fd, name);
-        }
-    }
-
-    private static class DefaultOptionsHolder {
-        static final Set<SocketOption<?>> defaultOptions = defaultOptions();
-
-        private static Set<SocketOption<?>> defaultOptions() {
-            HashSet<SocketOption<?>> set = new HashSet<>();
-            set.add(StandardSocketOptions.SO_SNDBUF);
-            set.add(StandardSocketOptions.SO_RCVBUF);
-            set.add(StandardSocketOptions.SO_KEEPALIVE);
-            set.add(StandardSocketOptions.SO_REUSEADDR);
-            if (Net.isReusePortAvailable()) {
-                set.add(StandardSocketOptions.SO_REUSEPORT);
-            }
-            set.add(StandardSocketOptions.SO_LINGER);
-            set.add(StandardSocketOptions.TCP_NODELAY);
-            // additional options required by socket adaptor
-            set.add(StandardSocketOptions.IP_TOS);
-            set.add(ExtendedSocketOption.SO_OOBINLINE);
-            set.addAll(ExtendedSocketOptions.clientSocketOptions());
-            return Collections.unmodifiableSet(set);
-        }
-    }
-
-    @Override
-    public final Set<SocketOption<?>> supportedOptions() {
-        return DefaultOptionsHolder.defaultOptions;
-    }
-
     /**
      * Marks the beginning of a read operation that might block.
      *
@@ -433,7 +297,7 @@
      * @throws ClosedChannelException if the channel is closed or output shutdown
      * @throws NotYetConnectedException if the channel is not yet connected
      */
-    private void beginWrite(boolean blocking) throws ClosedChannelException {
+    void beginWrite(boolean blocking) throws ClosedChannelException {
         if (blocking) {
             // set hook for Thread.interrupt
             begin();
@@ -456,7 +320,7 @@
      * @throws AsynchronousCloseException if the channel was closed due to this
      * thread being interrupted on a blocking write operation.
      */
-    private void endWrite(boolean blocking, boolean completed)
+    void endWrite(boolean blocking, boolean completed)
         throws AsynchronousCloseException
     {
         if (blocking) {
@@ -529,34 +393,6 @@
         }
     }
 
-    /**
-     * Writes a byte of out of band data.
-     */
-    int sendOutOfBandData(byte b) throws IOException {
-        writeLock.lock();
-        try {
-            boolean blocking = isBlocking();
-            int n = 0;
-            try {
-                beginWrite(blocking);
-                if (blocking) {
-                    do {
-                        n = Net.sendOOB(fd, b);
-                    } while (n == IOStatus.INTERRUPTED && isOpen());
-                } else {
-                    n = Net.sendOOB(fd, b);
-                }
-            } finally {
-                endWrite(blocking, n > 0);
-                if (n <= 0 && isOutputClosed)
-                    throw new AsynchronousCloseException();
-            }
-            return IOStatus.normalize(n);
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
     @Override
     protected void implConfigureBlocking(boolean block) throws IOException {
         readLock.lock();
@@ -586,7 +422,7 @@
     /**
      * Returns the local address, or null if not bound
      */
-    InetSocketAddress localAddress() {
+    SocketAddress localAddress() {
         synchronized (stateLock) {
             return localAddress;
         }
@@ -595,44 +431,13 @@
     /**
      * Returns the remote address, or null if not connected
      */
-    InetSocketAddress remoteAddress() {
+    SocketAddress remoteAddress() {
         synchronized (stateLock) {
             return remoteAddress;
         }
     }
 
     @Override
-    public SocketChannel bind(SocketAddress local) throws IOException {
-        readLock.lock();
-        try {
-            writeLock.lock();
-            try {
-                synchronized (stateLock) {
-                    ensureOpen();
-                    if (state == ST_CONNECTIONPENDING)
-                        throw new ConnectionPendingException();
-                    if (localAddress != null)
-                        throw new AlreadyBoundException();
-                    InetSocketAddress isa = (local == null) ?
-                        new InetSocketAddress(0) : Net.checkAddress(local);
-                    SecurityManager sm = System.getSecurityManager();
-                    if (sm != null) {
-                        sm.checkListen(isa.getPort());
-                    }
-                    NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
-                    Net.bind(fd, isa.getAddress(), isa.getPort());
-                    localAddress = Net.localAddress(fd);
-                }
-            } finally {
-                writeLock.unlock();
-            }
-        } finally {
-            readLock.unlock();
-        }
-        return this;
-    }
-
-    @Override
     public boolean isConnected() {
         return (state == ST_CONNECTED);
     }
@@ -651,7 +456,7 @@
      * @throws ConnectionPendingException is a connection is pending
      * @throws IOException if the pre-connect hook fails
      */
-    private void beginConnect(boolean blocking, InetSocketAddress isa)
+    private void beginConnect(boolean blocking, SocketAddress sa)
         throws IOException
     {
         if (blocking) {
@@ -668,9 +473,11 @@
             assert state == ST_UNCONNECTED;
             this.state = ST_CONNECTIONPENDING;
 
-            if (localAddress == null)
+            if (localAddress == null && sa instanceof InetSocketAddress) {
+                InetSocketAddress isa = (InetSocketAddress)sa;
                 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort());
-            remoteAddress = isa;
+            }
+            remoteAddress = sa;
 
             if (blocking) {
                 // record thread so it can be signalled if needed
@@ -686,7 +493,7 @@
      * thread being interrupted on a blocking connect operation.
      * @throws IOException if completed and unable to obtain the local address
      */
-    private void endConnect(boolean blocking, boolean completed)
+    void endConnect(boolean blocking, boolean completed)
         throws IOException
     {
         endRead(blocking, completed);
@@ -694,7 +501,7 @@
         if (completed) {
             synchronized (stateLock) {
                 if (state == ST_CONNECTIONPENDING) {
-                    localAddress = Net.localAddress(fd);
+                    localAddress = localAddressImpl(fd);
                     state = ST_CONNECTED;
                 }
             }
@@ -704,22 +511,13 @@
     /**
      * Checks the remote address to which this channel is to be connected.
      */
-    private InetSocketAddress checkRemote(SocketAddress sa) throws IOException {
-        InetSocketAddress isa = Net.checkAddress(sa);
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
-        }
-        if (isa.getAddress().isAnyLocalAddress()) {
-            return new InetSocketAddress(InetAddress.getLocalHost(), isa.getPort());
-        } else {
-            return isa;
-        }
-    }
+    abstract SocketAddress checkRemote(SocketAddress sa) throws IOException;
+
+    abstract int connectImpl(FileDescriptor fd,SocketAddress sa) throws IOException;
 
     @Override
     public boolean connect(SocketAddress remote) throws IOException {
-        InetSocketAddress isa = checkRemote(remote);
+        SocketAddress sa = checkRemote(remote);
         try {
             readLock.lock();
             try {
@@ -728,8 +526,8 @@
                     boolean blocking = isBlocking();
                     boolean connected = false;
                     try {
-                        beginConnect(blocking, isa);
-                        int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                        beginConnect(blocking, sa);
+                        int n = connectImpl(fd, sa);
                         if (n > 0) {
                             connected = true;
                         } else if (blocking) {
@@ -754,7 +552,7 @@
         } catch (IOException ioe) {
             // connect failed, close the channel
             close();
-            throw SocketExceptions.of(ioe, isa);
+            throw SocketExceptions.of(ioe, sa);
         }
     }
 
@@ -780,6 +578,9 @@
         }
     }
 
+    abstract SocketAddress localAddressImpl(FileDescriptor fd)
+        throws IOException;
+
     /**
      * Marks the end of a finishConnect operation that may have blocked.
      *
@@ -795,7 +596,7 @@
         if (completed) {
             synchronized (stateLock) {
                 if (state == ST_CONNECTIONPENDING) {
-                    localAddress = Net.localAddress(fd);
+                    localAddress = localAddressImpl(fd);
                     state = ST_CONNECTED;
                 }
             }
@@ -964,6 +765,13 @@
         }
     }
 
+    /**
+     * Package private version called from InheritedChannel
+     */
+    void localImplCloseSelectableChannel() throws IOException {
+        implCloseSelectableChannel();
+    }
+
     @Override
     public SocketChannel shutdownInput() throws IOException {
         synchronized (stateLock) {
@@ -1034,7 +842,7 @@
      * @throws SocketTimeoutException if the read timeout elapses
      */
     void blockingConnect(SocketAddress remote, long nanos) throws IOException {
-        InetSocketAddress isa = checkRemote(remote);
+        SocketAddress sa = checkRemote(remote);
         try {
             readLock.lock();
             try {
@@ -1044,11 +852,11 @@
                         throw new IllegalBlockingModeException();
                     boolean connected = false;
                     try {
-                        beginConnect(true, isa);
+                        beginConnect(true, sa);
                         // change socket to non-blocking
                         lockedConfigureBlocking(false);
                         try {
-                            int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                            int n = connectImpl(fd, sa);
                             connected = (n > 0) ? true : finishTimedConnect(nanos);
                         } finally {
                             // restore socket to blocking mode
@@ -1066,7 +874,7 @@
         } catch (IOException ioe) {
             // connect failed, close the channel
             close();
-            throw SocketExceptions.of(ioe, isa);
+            throw SocketExceptions.of(ioe, sa);
         }
     }
 
@@ -1309,6 +1117,8 @@
         return fdVal;
     }
 
+    abstract String getRevealedLocalAddressAsString(SocketAddress sa);
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -1333,10 +1143,10 @@
                         sb.append(" oshut");
                     break;
                 }
-                InetSocketAddress addr = localAddress();
+                SocketAddress addr = localAddress();
                 if (addr != null) {
                     sb.append(" local=");
-                    sb.append(Net.getRevealedLocalAddressAsString(addr));
+                    sb.append(getRevealedLocalAddressAsString(addr));
                 }
                 if (remoteAddress() != null) {
                     sb.append(" remote=");