jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
changeset 25859 3317bb8137f4
parent 25170 f58832169add
child 29986 97167d851fc4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.nio.channels.spi.*;
+import java.util.*;
+import sun.net.NetHooks;
+import sun.net.ExtendedOptionsImpl;
+
+
+/**
+ * An implementation of SocketChannels
+ */
+
+class SocketChannelImpl
+    extends SocketChannel
+    implements SelChImpl
+{
+
+    // Used to make native read and write calls
+    private static NativeDispatcher nd;
+
+    // Our file descriptor object
+    private final FileDescriptor fd;
+
+    // fd value needed for dev/poll. This value will remain valid
+    // even after the value in the file descriptor object has been set to -1
+    private final int fdVal;
+
+    // IDs of native threads doing reads and writes, for signalling
+    private volatile long readerThread = 0;
+    private volatile long writerThread = 0;
+
+    // Lock held by current reading or connecting thread
+    private final Object readLock = new Object();
+
+    // Lock held by current writing or connecting thread
+    private final Object writeLock = new Object();
+
+    // 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();
+
+    // -- 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_UNINITIALIZED = -1;
+    private static final int ST_UNCONNECTED = 0;
+    private static final int ST_PENDING = 1;
+    private static final int ST_CONNECTED = 2;
+    private static final int ST_KILLPENDING = 3;
+    private static final int ST_KILLED = 4;
+    private int state = ST_UNINITIALIZED;
+
+    // Binding
+    private InetSocketAddress localAddress;
+    private InetSocketAddress remoteAddress;
+
+    // Input/Output open
+    private boolean isInputOpen = true;
+    private boolean isOutputOpen = true;
+    private boolean readyToConnect = false;
+
+    // Socket adaptor, created on demand
+    private Socket socket;
+
+    // -- End of fields protected by stateLock
+
+
+    // Constructor for normal connecting sockets
+    //
+    SocketChannelImpl(SelectorProvider sp) throws IOException {
+        super(sp);
+        this.fd = Net.socket(true);
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ST_UNCONNECTED;
+    }
+
+    SocketChannelImpl(SelectorProvider sp,
+                      FileDescriptor fd,
+                      boolean bound)
+        throws IOException
+    {
+        super(sp);
+        this.fd = fd;
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ST_UNCONNECTED;
+        if (bound)
+            this.localAddress = Net.localAddress(fd);
+    }
+
+    // Constructor for sockets obtained from server sockets
+    //
+    SocketChannelImpl(SelectorProvider sp,
+                      FileDescriptor fd, InetSocketAddress remote)
+        throws IOException
+    {
+        super(sp);
+        this.fd = fd;
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ST_CONNECTED;
+        this.localAddress = Net.localAddress(fd);
+        this.remoteAddress = remote;
+    }
+
+    public Socket socket() {
+        synchronized (stateLock) {
+            if (socket == null)
+                socket = SocketAdaptor.create(this);
+            return socket;
+        }
+    }
+
+    @Override
+    public SocketAddress getLocalAddress() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            return  Net.getRevealedLocalAddress(localAddress);
+        }
+    }
+
+    @Override
+    public SocketAddress getRemoteAddress() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            return remoteAddress;
+        }
+    }
+
+    @Override
+    public <T> SocketChannel setOption(SocketOption<T> name, T value)
+        throws IOException
+    {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            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, Net.UNSPEC, name, value);
+            return this;
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getOption(SocketOption<T> name)
+        throws IOException
+    {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            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, Net.UNSPEC, name);
+        }
+    }
+
+    private static class DefaultOptionsHolder {
+        static final Set<SocketOption<?>> defaultOptions = defaultOptions();
+
+        private static Set<SocketOption<?>> defaultOptions() {
+            HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(8);
+            set.add(StandardSocketOptions.SO_SNDBUF);
+            set.add(StandardSocketOptions.SO_RCVBUF);
+            set.add(StandardSocketOptions.SO_KEEPALIVE);
+            set.add(StandardSocketOptions.SO_REUSEADDR);
+            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);
+            if (ExtendedOptionsImpl.flowSupported()) {
+                set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA);
+            }
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    @Override
+    public final Set<SocketOption<?>> supportedOptions() {
+        return DefaultOptionsHolder.defaultOptions;
+    }
+
+    private boolean ensureReadOpen() throws ClosedChannelException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+            if (!isInputOpen)
+                return false;
+            else
+                return true;
+        }
+    }
+
+    private void ensureWriteOpen() throws ClosedChannelException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isOutputOpen)
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+        }
+    }
+
+    private void readerCleanup() throws IOException {
+        synchronized (stateLock) {
+            readerThread = 0;
+            if (state == ST_KILLPENDING)
+                kill();
+        }
+    }
+
+    private void writerCleanup() throws IOException {
+        synchronized (stateLock) {
+            writerThread = 0;
+            if (state == ST_KILLPENDING)
+                kill();
+        }
+    }
+
+    public int read(ByteBuffer buf) throws IOException {
+
+        if (buf == null)
+            throw new NullPointerException();
+
+        synchronized (readLock) {
+            if (!ensureReadOpen())
+                return -1;
+            int n = 0;
+            try {
+
+                // Set up the interruption machinery; see
+                // AbstractInterruptibleChannel for details
+                //
+                begin();
+
+                synchronized (stateLock) {
+                    if (!isOpen()) {
+                    // Either the current thread is already interrupted, so
+                    // begin() closed the channel, or another thread closed the
+                    // channel since we checked it a few bytecodes ago.  In
+                    // either case the value returned here is irrelevant since
+                    // the invocation of end() in the finally block will throw
+                    // an appropriate exception.
+                    //
+                        return 0;
+
+                    }
+
+                    // Save this thread so that it can be signalled on those
+                    // platforms that require it
+                    //
+                    readerThread = NativeThread.current();
+                }
+
+                // Between the previous test of isOpen() and the return of the
+                // IOUtil.read invocation below, this channel might be closed
+                // or this thread might be interrupted.  We rely upon the
+                // implicit synchronization point in the kernel read() call to
+                // make sure that the right thing happens.  In either case the
+                // implCloseSelectableChannel method is ultimately invoked in
+                // some other thread, so there are three possibilities:
+                //
+                //   - implCloseSelectableChannel() invokes nd.preClose()
+                //     before this thread invokes read(), in which case the
+                //     read returns immediately with either EOF or an error,
+                //     the latter of which will cause an IOException to be
+                //     thrown.
+                //
+                //   - implCloseSelectableChannel() invokes nd.preClose() after
+                //     this thread is blocked in read().  On some operating
+                //     systems (e.g., Solaris and Windows) this causes the read
+                //     to return immediately with either EOF or an error
+                //     indication.
+                //
+                //   - implCloseSelectableChannel() invokes nd.preClose() after
+                //     this thread is blocked in read() but the operating
+                //     system (e.g., Linux) doesn't support preemptive close,
+                //     so implCloseSelectableChannel() proceeds to signal this
+                //     thread, thereby causing the read to return immediately
+                //     with IOStatus.INTERRUPTED.
+                //
+                // In all three cases the invocation of end() in the finally
+                // clause will notice that the channel has been closed and
+                // throw an appropriate exception (AsynchronousCloseException
+                // or ClosedByInterruptException) if necessary.
+                //
+                // *There is A fourth possibility. implCloseSelectableChannel()
+                // invokes nd.preClose(), signals reader/writer thred and quickly
+                // moves on to nd.close() in kill(), which does a real close.
+                // Then a third thread accepts a new connection, opens file or
+                // whatever that causes the released "fd" to be recycled. All
+                // above happens just between our last isOpen() check and the
+                // next kernel read reached, with the recycled "fd". The solution
+                // is to postpone the real kill() if there is a reader or/and
+                // writer thread(s) over there "waiting", leave the cleanup/kill
+                // to the reader or writer thread. (the preClose() still happens
+                // so the connection gets cut off as usual).
+                //
+                // For socket channels there is the additional wrinkle that
+                // asynchronous shutdown works much like asynchronous close,
+                // except that the channel is shutdown rather than completely
+                // closed.  This is analogous to the first two cases above,
+                // except that the shutdown operation plays the role of
+                // nd.preClose().
+                for (;;) {
+                    n = IOUtil.read(fd, buf, -1, nd);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen()) {
+                        // The system call was interrupted but the channel
+                        // is still open, so retry
+                        continue;
+                    }
+                    return IOStatus.normalize(n);
+                }
+
+            } finally {
+                readerCleanup();        // Clear reader thread
+                // The end method, which is defined in our superclass
+                // AbstractInterruptibleChannel, resets the interruption
+                // machinery.  If its argument is true then it returns
+                // normally; otherwise it checks the interrupt and open state
+                // of this channel and throws an appropriate exception if
+                // necessary.
+                //
+                // So, if we actually managed to do any I/O in the above try
+                // block then we pass true to the end method.  We also pass
+                // true if the channel was in non-blocking mode when the I/O
+                // operation was initiated but no data could be transferred;
+                // this prevents spurious exceptions from being thrown in the
+                // rare event that a channel is closed or a thread is
+                // interrupted at the exact moment that a non-blocking I/O
+                // request is made.
+                //
+                end(n > 0 || (n == IOStatus.UNAVAILABLE));
+
+                // Extra case for socket channels: Asynchronous shutdown
+                //
+                synchronized (stateLock) {
+                    if ((n <= 0) && (!isInputOpen))
+                        return IOStatus.EOF;
+                }
+
+                assert IOStatus.check(n);
+
+            }
+        }
+    }
+
+    public long read(ByteBuffer[] dsts, int offset, int length)
+        throws IOException
+    {
+        if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
+            throw new IndexOutOfBoundsException();
+        synchronized (readLock) {
+            if (!ensureReadOpen())
+                return -1;
+            long n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    readerThread = NativeThread.current();
+                }
+
+                for (;;) {
+                    n = IOUtil.read(fd, dsts, offset, length, nd);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen())
+                        continue;
+                    return IOStatus.normalize(n);
+                }
+            } finally {
+                readerCleanup();
+                end(n > 0 || (n == IOStatus.UNAVAILABLE));
+                synchronized (stateLock) {
+                    if ((n <= 0) && (!isInputOpen))
+                        return IOStatus.EOF;
+                }
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    public int write(ByteBuffer buf) throws IOException {
+        if (buf == null)
+            throw new NullPointerException();
+        synchronized (writeLock) {
+            ensureWriteOpen();
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    writerThread = NativeThread.current();
+                }
+                for (;;) {
+                    n = IOUtil.write(fd, buf, -1, nd);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen())
+                        continue;
+                    return IOStatus.normalize(n);
+                }
+            } finally {
+                writerCleanup();
+                end(n > 0 || (n == IOStatus.UNAVAILABLE));
+                synchronized (stateLock) {
+                    if ((n <= 0) && (!isOutputOpen))
+                        throw new AsynchronousCloseException();
+                }
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    public long write(ByteBuffer[] srcs, int offset, int length)
+        throws IOException
+    {
+        if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
+            throw new IndexOutOfBoundsException();
+        synchronized (writeLock) {
+            ensureWriteOpen();
+            long n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    writerThread = NativeThread.current();
+                }
+                for (;;) {
+                    n = IOUtil.write(fd, srcs, offset, length, nd);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen())
+                        continue;
+                    return IOStatus.normalize(n);
+                }
+            } finally {
+                writerCleanup();
+                end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                synchronized (stateLock) {
+                    if ((n <= 0) && (!isOutputOpen))
+                        throw new AsynchronousCloseException();
+                }
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    // package-private
+    int sendOutOfBandData(byte b) throws IOException {
+        synchronized (writeLock) {
+            ensureWriteOpen();
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    writerThread = NativeThread.current();
+                }
+                for (;;) {
+                    n = sendOutOfBandData(fd, b);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen())
+                        continue;
+                    return IOStatus.normalize(n);
+                }
+            } finally {
+                writerCleanup();
+                end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                synchronized (stateLock) {
+                    if ((n <= 0) && (!isOutputOpen))
+                        throw new AsynchronousCloseException();
+                }
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        IOUtil.configureBlocking(fd, block);
+    }
+
+    public InetSocketAddress localAddress() {
+        synchronized (stateLock) {
+            return localAddress;
+        }
+    }
+
+    public SocketAddress remoteAddress() {
+        synchronized (stateLock) {
+            return remoteAddress;
+        }
+    }
+
+    @Override
+    public SocketChannel bind(SocketAddress local) throws IOException {
+        synchronized (readLock) {
+            synchronized (writeLock) {
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    if (state == ST_PENDING)
+                        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);
+                }
+            }
+        }
+        return this;
+    }
+
+    public boolean isConnected() {
+        synchronized (stateLock) {
+            return (state == ST_CONNECTED);
+        }
+    }
+
+    public boolean isConnectionPending() {
+        synchronized (stateLock) {
+            return (state == ST_PENDING);
+        }
+    }
+
+    void ensureOpenAndUnconnected() throws IOException { // package-private
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (state == ST_CONNECTED)
+                throw new AlreadyConnectedException();
+            if (state == ST_PENDING)
+                throw new ConnectionPendingException();
+        }
+    }
+
+    public boolean connect(SocketAddress sa) throws IOException {
+        int localPort = 0;
+
+        synchronized (readLock) {
+            synchronized (writeLock) {
+                ensureOpenAndUnconnected();
+                InetSocketAddress isa = Net.checkAddress(sa);
+                SecurityManager sm = System.getSecurityManager();
+                if (sm != null)
+                    sm.checkConnect(isa.getAddress().getHostAddress(),
+                                    isa.getPort());
+                synchronized (blockingLock()) {
+                    int n = 0;
+                    try {
+                        try {
+                            begin();
+                            synchronized (stateLock) {
+                                if (!isOpen()) {
+                                    return false;
+                                }
+                                // notify hook only if unbound
+                                if (localAddress == null) {
+                                    NetHooks.beforeTcpConnect(fd,
+                                                           isa.getAddress(),
+                                                           isa.getPort());
+                                }
+                                readerThread = NativeThread.current();
+                            }
+                            for (;;) {
+                                InetAddress ia = isa.getAddress();
+                                if (ia.isAnyLocalAddress())
+                                    ia = InetAddress.getLocalHost();
+                                n = Net.connect(fd,
+                                                ia,
+                                                isa.getPort());
+                                if (  (n == IOStatus.INTERRUPTED)
+                                      && isOpen())
+                                    continue;
+                                break;
+                            }
+
+                        } finally {
+                            readerCleanup();
+                            end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                            assert IOStatus.check(n);
+                        }
+                    } catch (IOException x) {
+                        // If an exception was thrown, close the channel after
+                        // invoking end() so as to avoid bogus
+                        // AsynchronousCloseExceptions
+                        close();
+                        throw x;
+                    }
+                    synchronized (stateLock) {
+                        remoteAddress = isa;
+                        if (n > 0) {
+
+                            // Connection succeeded; disallow further
+                            // invocation
+                            state = ST_CONNECTED;
+                            if (isOpen())
+                                localAddress = Net.localAddress(fd);
+                            return true;
+                        }
+                        // If nonblocking and no exception then connection
+                        // pending; disallow another invocation
+                        if (!isBlocking())
+                            state = ST_PENDING;
+                        else
+                            assert false;
+                    }
+                }
+                return false;
+            }
+        }
+    }
+
+    public boolean finishConnect() throws IOException {
+        synchronized (readLock) {
+            synchronized (writeLock) {
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    if (state == ST_CONNECTED)
+                        return true;
+                    if (state != ST_PENDING)
+                        throw new NoConnectionPendingException();
+                }
+                int n = 0;
+                try {
+                    try {
+                        begin();
+                        synchronized (blockingLock()) {
+                            synchronized (stateLock) {
+                                if (!isOpen()) {
+                                    return false;
+                                }
+                                readerThread = NativeThread.current();
+                            }
+                            if (!isBlocking()) {
+                                for (;;) {
+                                    n = checkConnect(fd, false,
+                                                     readyToConnect);
+                                    if (  (n == IOStatus.INTERRUPTED)
+                                          && isOpen())
+                                        continue;
+                                    break;
+                                }
+                            } else {
+                                for (;;) {
+                                    n = checkConnect(fd, true,
+                                                     readyToConnect);
+                                    if (n == 0) {
+                                        // Loop in case of
+                                        // spurious notifications
+                                        continue;
+                                    }
+                                    if (  (n == IOStatus.INTERRUPTED)
+                                          && isOpen())
+                                        continue;
+                                    break;
+                                }
+                            }
+                        }
+                    } finally {
+                        synchronized (stateLock) {
+                            readerThread = 0;
+                            if (state == ST_KILLPENDING) {
+                                kill();
+                                // poll()/getsockopt() does not report
+                                // error (throws exception, with n = 0)
+                                // on Linux platform after dup2 and
+                                // signal-wakeup. Force n to 0 so the
+                                // end() can throw appropriate exception
+                                n = 0;
+                            }
+                        }
+                        end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                        assert IOStatus.check(n);
+                    }
+                } catch (IOException x) {
+                    // If an exception was thrown, close the channel after
+                    // invoking end() so as to avoid bogus
+                    // AsynchronousCloseExceptions
+                    close();
+                    throw x;
+                }
+                if (n > 0) {
+                    synchronized (stateLock) {
+                        state = ST_CONNECTED;
+                        if (isOpen())
+                            localAddress = Net.localAddress(fd);
+                    }
+                    return true;
+                }
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public SocketChannel shutdownInput() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+            if (isInputOpen) {
+                Net.shutdown(fd, Net.SHUT_RD);
+                if (readerThread != 0)
+                    NativeThread.signal(readerThread);
+                isInputOpen = false;
+            }
+            return this;
+        }
+    }
+
+    @Override
+    public SocketChannel shutdownOutput() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+            if (isOutputOpen) {
+                Net.shutdown(fd, Net.SHUT_WR);
+                if (writerThread != 0)
+                    NativeThread.signal(writerThread);
+                isOutputOpen = false;
+            }
+            return this;
+        }
+    }
+
+    public boolean isInputOpen() {
+        synchronized (stateLock) {
+            return isInputOpen;
+        }
+    }
+
+    public boolean isOutputOpen() {
+        synchronized (stateLock) {
+            return isOutputOpen;
+        }
+    }
+
+    // AbstractInterruptibleChannel synchronizes invocations of this method
+    // using AbstractInterruptibleChannel.closeLock, and also ensures that this
+    // method is only ever invoked once.  Before we get to this method, isOpen
+    // (which is volatile) will have been set to false.
+    //
+    protected void implCloseSelectableChannel() throws IOException {
+        synchronized (stateLock) {
+            isInputOpen = false;
+            isOutputOpen = false;
+
+            // Close the underlying file descriptor and dup it to a known fd
+            // that's already closed.  This prevents other operations on this
+            // channel from using the old fd, which might be recycled in the
+            // meantime and allocated to an entirely different channel.
+            //
+            if (state != ST_KILLED)
+                nd.preClose(fd);
+
+            // Signal native threads, if needed.  If a target thread is not
+            // currently blocked in an I/O operation then no harm is done since
+            // the signal handler doesn't actually do anything.
+            //
+            if (readerThread != 0)
+                NativeThread.signal(readerThread);
+
+            if (writerThread != 0)
+                NativeThread.signal(writerThread);
+
+            // If this channel is not registered then it's safe to close the fd
+            // immediately since we know at this point that no thread is
+            // blocked in an I/O operation upon the channel and, since the
+            // channel is marked closed, no thread will start another such
+            // operation.  If this channel is registered then we don't close
+            // the fd since it might be in use by a selector.  In that case
+            // closing this channel caused its keys to be cancelled, so the
+            // last selector to deregister a key for this channel will invoke
+            // kill() to close the fd.
+            //
+            if (!isRegistered())
+                kill();
+        }
+    }
+
+    public void kill() throws IOException {
+        synchronized (stateLock) {
+            if (state == ST_KILLED)
+                return;
+            if (state == ST_UNINITIALIZED) {
+                state = ST_KILLED;
+                return;
+            }
+            assert !isOpen() && !isRegistered();
+
+            // Postpone the kill if there is a waiting reader
+            // or writer thread. See the comments in read() for
+            // more detailed explanation.
+            if (readerThread == 0 && writerThread == 0) {
+                nd.close(fd);
+                state = ST_KILLED;
+            } else {
+                state = ST_KILLPENDING;
+            }
+        }
+    }
+
+    /**
+     * Translates native poll revent ops into a ready operation ops
+     */
+    public boolean translateReadyOps(int ops, int initialOps,
+                                     SelectionKeyImpl sk) {
+        int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
+        int oldOps = sk.nioReadyOps();
+        int newOps = initialOps;
+
+        if ((ops & Net.POLLNVAL) != 0) {
+            // This should only happen if this channel is pre-closed while a
+            // selection operation is in progress
+            // ## Throw an error if this channel has not been pre-closed
+            return false;
+        }
+
+        if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
+            newOps = intOps;
+            sk.nioReadyOps(newOps);
+            // No need to poll again in checkConnect,
+            // the error will be detected there
+            readyToConnect = true;
+            return (newOps & ~oldOps) != 0;
+        }
+
+        if (((ops & Net.POLLIN) != 0) &&
+            ((intOps & SelectionKey.OP_READ) != 0) &&
+            (state == ST_CONNECTED))
+            newOps |= SelectionKey.OP_READ;
+
+        if (((ops & Net.POLLCONN) != 0) &&
+            ((intOps & SelectionKey.OP_CONNECT) != 0) &&
+            ((state == ST_UNCONNECTED) || (state == ST_PENDING))) {
+            newOps |= SelectionKey.OP_CONNECT;
+            readyToConnect = true;
+        }
+
+        if (((ops & Net.POLLOUT) != 0) &&
+            ((intOps & SelectionKey.OP_WRITE) != 0) &&
+            (state == ST_CONNECTED))
+            newOps |= SelectionKey.OP_WRITE;
+
+        sk.nioReadyOps(newOps);
+        return (newOps & ~oldOps) != 0;
+    }
+
+    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
+    }
+
+    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, 0, sk);
+    }
+
+    // package-private
+    int poll(int events, long timeout) throws IOException {
+        assert Thread.holdsLock(blockingLock()) && !isBlocking();
+
+        synchronized (readLock) {
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    readerThread = NativeThread.current();
+                }
+                n = Net.poll(fd, events, timeout);
+            } finally {
+                readerCleanup();
+                end(n > 0);
+            }
+            return n;
+        }
+    }
+
+    /**
+     * Translates an interest operation set into a native poll event set
+     */
+    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+        int newOps = 0;
+        if ((ops & SelectionKey.OP_READ) != 0)
+            newOps |= Net.POLLIN;
+        if ((ops & SelectionKey.OP_WRITE) != 0)
+            newOps |= Net.POLLOUT;
+        if ((ops & SelectionKey.OP_CONNECT) != 0)
+            newOps |= Net.POLLCONN;
+        sk.selector.putEventOps(sk, newOps);
+    }
+
+    public FileDescriptor getFD() {
+        return fd;
+    }
+
+    public int getFDVal() {
+        return fdVal;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(this.getClass().getSuperclass().getName());
+        sb.append('[');
+        if (!isOpen())
+            sb.append("closed");
+        else {
+            synchronized (stateLock) {
+                switch (state) {
+                case ST_UNCONNECTED:
+                    sb.append("unconnected");
+                    break;
+                case ST_PENDING:
+                    sb.append("connection-pending");
+                    break;
+                case ST_CONNECTED:
+                    sb.append("connected");
+                    if (!isInputOpen)
+                        sb.append(" ishut");
+                    if (!isOutputOpen)
+                        sb.append(" oshut");
+                    break;
+                }
+                InetSocketAddress addr = localAddress();
+                if (addr != null) {
+                    sb.append(" local=");
+                    sb.append(Net.getRevealedLocalAddressAsString(addr));
+                }
+                if (remoteAddress() != null) {
+                    sb.append(" remote=");
+                    sb.append(remoteAddress().toString());
+                }
+            }
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+
+    // -- Native methods --
+
+    private static native int checkConnect(FileDescriptor fd,
+                                           boolean block, boolean ready)
+        throws IOException;
+
+    private static native int sendOutOfBandData(FileDescriptor fd, byte data)
+        throws IOException;
+
+    static {
+        IOUtil.load();
+        nd = new SocketDispatcher();
+    }
+
+}