1136 lines · 37.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 2011, 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.nio.ByteBuffer;import java.nio.MappedByteBuffer;import java.nio.channels.*;import java.util.ArrayList;import java.util.List;import java.security.AccessController;import sun.misc.Cleaner;import sun.security.action.GetPropertyAction; public class FileChannelImpl    extends FileChannel{    // Memory allocation size for mapping buffers    private static final long allocationGranularity;     // Used to make native read and write calls    private final FileDispatcher nd;     // File descriptor    private final FileDescriptor fd;     // File access mode (immutable)    private final boolean writable;    private final boolean readable;    private final boolean append;     // Required to prevent finalization of creating stream (immutable)    private final Object parent;     // Thread-safe set of IDs of native threads, for signalling    private final NativeThreadSet threads = new NativeThreadSet(2);     // Lock for operations involving position and size    private final Object positionLock = new Object();     private FileChannelImpl(FileDescriptor fd, boolean readable,                            boolean writable, boolean append, Object parent)    {        this.fd = fd;        this.readable = readable;        this.writable = writable;        this.append = append;        this.parent = parent;        this.nd = new FileDispatcherImpl(append);    }     // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()    public static FileChannel open(FileDescriptor fd,                                   boolean readable, boolean writable,                                   Object parent)    {        return new FileChannelImpl(fd, readable, writable, false, parent);    }     // Used by FileOutputStream.getChannel    public static FileChannel open(FileDescriptor fd,                                   boolean readable, boolean writable,                                   boolean append, Object parent)    {        return new FileChannelImpl(fd, readable, writable, append, parent);    }     private void ensureOpen() throws IOException {        if (!isOpen())            throw new ClosedChannelException();    }      // -- Standard channel operations --     protected void implCloseChannel() throws IOException {        // Release and invalidate any locks that we still hold        if (fileLockTable != null) {            for (FileLock fl: fileLockTable.removeAll()) {                synchronized (fl) {                    if (fl.isValid()) {                        nd.release(fd, fl.position(), fl.size());                        ((FileLockImpl)fl).invalidate();                    }                }            }        }         nd.preClose(fd);        threads.signalAndWait();         if (parent != null) {             // Close the fd via the parent stream's close method.  The parent            // will reinvoke our close method, which is defined in the            // superclass AbstractInterruptibleChannel, but the isOpen logic in            // that method will prevent this method from being reinvoked.            //            ((java.io.Closeable)parent).close();        } else {            nd.close(fd);        }     }     public int read(ByteBuffer dst) throws IOException {        ensureOpen();        if (!readable)            throw new NonReadableChannelException();        synchronized (positionLock) {            int n = 0;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return 0;                do {                    n = IOUtil.read(fd, dst, -1, nd, positionLock);                } while ((n == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(n);            } finally {                threads.remove(ti);                end(n > 0);                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();        ensureOpen();        if (!readable)            throw new NonReadableChannelException();        synchronized (positionLock) {            long n = 0;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return 0;                do {                    n = IOUtil.read(fd, dsts, offset, length, nd);                } while ((n == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(n);            } finally {                threads.remove(ti);                end(n > 0);                assert IOStatus.check(n);            }        }    }     public int write(ByteBuffer src) throws IOException {        ensureOpen();        if (!writable)            throw new NonWritableChannelException();        synchronized (positionLock) {            int n = 0;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return 0;                do {                    n = IOUtil.write(fd, src, -1, nd, positionLock);                } while ((n == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(n);            } finally {                threads.remove(ti);                end(n > 0);                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();        ensureOpen();        if (!writable)            throw new NonWritableChannelException();        synchronized (positionLock) {            long n = 0;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return 0;                do {                    n = IOUtil.write(fd, srcs, offset, length, nd);                } while ((n == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(n);            } finally {                threads.remove(ti);                end(n > 0);                assert IOStatus.check(n);            }        }    }     // -- Other operations --     public long position() throws IOException {        ensureOpen();        synchronized (positionLock) {            long p = -1;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return 0;                do {                    // in append-mode then position is advanced to end before writing                    p = (append) ? nd.size(fd) : position0(fd, -1);                } while ((p == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(p);            } finally {                threads.remove(ti);                end(p > -1);                assert IOStatus.check(p);            }        }    }     public FileChannel position(long newPosition) throws IOException {        ensureOpen();        if (newPosition < 0)            throw new IllegalArgumentException();        synchronized (positionLock) {            long p = -1;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return null;                do {                    p  = position0(fd, newPosition);                } while ((p == IOStatus.INTERRUPTED) && isOpen());                return this;            } finally {                threads.remove(ti);                end(p > -1);                assert IOStatus.check(p);            }        }    }     public long size() throws IOException {        ensureOpen();        synchronized (positionLock) {            long s = -1;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return -1;                do {                    s = nd.size(fd);                } while ((s == IOStatus.INTERRUPTED) && isOpen());                return IOStatus.normalize(s);            } finally {                threads.remove(ti);                end(s > -1);                assert IOStatus.check(s);            }        }    }     public FileChannel truncate(long size) throws IOException {        ensureOpen();        if (size < 0)            throw new IllegalArgumentException();        if (size > size())            return this;        if (!writable)            throw new NonWritableChannelException();        synchronized (positionLock) {            int rv = -1;            long p = -1;            int ti = -1;            try {                begin();                ti = threads.add();                if (!isOpen())                    return null;                 // get current position                do {                    p = position0(fd, -1);                } while ((p == IOStatus.INTERRUPTED) && isOpen());                if (!isOpen())                    return null;                assert p >= 0;                 // truncate file                do {                    rv = nd.truncate(fd, size);                } while ((rv == IOStatus.INTERRUPTED) && isOpen());                if (!isOpen())                    return null;                 // set position to size if greater than size                if (p > size)                    p = size;                do {                    rv = (int)position0(fd, p);                } while ((rv == IOStatus.INTERRUPTED) && isOpen());                return this;            } finally {                threads.remove(ti);                end(rv > -1);                assert IOStatus.check(rv);            }        }    }     public void force(boolean metaData) throws IOException {        ensureOpen();        int rv = -1;        int ti = -1;        try {            begin();            ti = threads.add();            if (!isOpen())                return;            do {                rv = nd.force(fd, metaData);            } while ((rv == IOStatus.INTERRUPTED) && isOpen());        } finally {            threads.remove(ti);            end(rv > -1);            assert IOStatus.check(rv);        }    }     // Assume at first that the underlying kernel supports sendfile();    // set this to false if we find out later that it doesn't    //    private static volatile boolean transferSupported = true;     // Assume that the underlying kernel sendfile() will work if the target    // fd is a pipe; set this to false if we find out later that it doesn't    //    private static volatile boolean pipeSupported = true;     // Assume that the underlying kernel sendfile() will work if the target    // fd is a file; set this to false if we find out later that it doesn't    //    private static volatile boolean fileSupported = true;     private long transferToDirectly(long position, int icount,                                    WritableByteChannel target)        throws IOException    {        if (!transferSupported)            return IOStatus.UNSUPPORTED;         FileDescriptor targetFD = null;        if (target instanceof FileChannelImpl) {            if (!fileSupported)                return IOStatus.UNSUPPORTED_CASE;            targetFD = ((FileChannelImpl)target).fd;        } else if (target instanceof SelChImpl) {            // Direct transfer to pipe causes EINVAL on some configurations            if ((target instanceof SinkChannelImpl) && !pipeSupported)                return IOStatus.UNSUPPORTED_CASE;            targetFD = ((SelChImpl)target).getFD();        }        if (targetFD == null)            return IOStatus.UNSUPPORTED;        int thisFDVal = IOUtil.fdVal(fd);        int targetFDVal = IOUtil.fdVal(targetFD);        if (thisFDVal == targetFDVal) // Not supported on some configurations            return IOStatus.UNSUPPORTED;         long n = -1;        int ti = -1;        try {            begin();            ti = threads.add();            if (!isOpen())                return -1;            do {                n = transferTo0(thisFDVal, position, icount, targetFDVal);            } while ((n == IOStatus.INTERRUPTED) && isOpen());            if (n == IOStatus.UNSUPPORTED_CASE) {                if (target instanceof SinkChannelImpl)                    pipeSupported = false;                if (target instanceof FileChannelImpl)                    fileSupported = false;                return IOStatus.UNSUPPORTED_CASE;            }            if (n == IOStatus.UNSUPPORTED) {                // Don't bother trying again                transferSupported = false;                return IOStatus.UNSUPPORTED;            }            return IOStatus.normalize(n);        } finally {            threads.remove(ti);            end (n > -1);        }    }     // Maximum size to map when using a mapped buffer    private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;     private long transferToTrustedChannel(long position, long count,                                          WritableByteChannel target)        throws IOException    {        boolean isSelChImpl = (target instanceof SelChImpl);        if (!((target instanceof FileChannelImpl) || isSelChImpl))            return IOStatus.UNSUPPORTED;         // Trusted target: Use a mapped buffer        long remaining = count;        while (remaining > 0L) {            long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);            try {                MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size);                try {                    // ## Bug: Closing this channel will not terminate the write                    int n = target.write(dbb);                    assert n >= 0;                    remaining -= n;                    if (isSelChImpl) {                        // one attempt to write to selectable channel                        break;                    }                    assert n > 0;                    position += n;                } finally {                    unmap(dbb);                }            } catch (ClosedByInterruptException e) {                // target closed by interrupt as ClosedByInterruptException needs                // to be thrown after closing this channel.                assert !target.isOpen();                try {                    close();                } catch (Throwable suppressed) {                    e.addSuppressed(suppressed);                }                throw e;            } catch (IOException ioe) {                // Only throw exception if no bytes have been written                if (remaining == count)                    throw ioe;                break;            }        }        return count - remaining;    }     private long transferToArbitraryChannel(long position, int icount,                                            WritableByteChannel target)        throws IOException    {        // Untrusted target: Use a newly-erased buffer        int c = Math.min(icount, TRANSFER_SIZE);        ByteBuffer bb = Util.getTemporaryDirectBuffer(c);        long tw = 0;                    // Total bytes written        long pos = position;

Lines 1–500 of 1136.