997 lines · 36.6 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, 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;  /** * 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     // 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 SocketAddress localAddress;    private SocketAddress 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 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();             // special handling for IP_TOS: no-op when IPv6            if (name == StandardSocketOptions.IP_TOS) {                if (!Net.isIPv6Available())                    Net.setSocketOption(fd, StandardProtocolFamily.INET, name, 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();             // special handling for IP_TOS: always return 0 when IPv6            if (name == StandardSocketOptions.IP_TOS) {                return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) :                    (T) Net.getSocketOption(fd, StandardProtocolFamily.INET, 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);            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, readLock);                    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, writeLock);                    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

Lines 1–500 of 997.