Save This Page
Home » openjdk-7 » sun » nio » ch » [javadoc | source]
    1   /*
    2    * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package sun.nio.ch;
   27   
   28   import java.io.FileDescriptor;
   29   import java.io.FileInputStream;
   30   import java.io.FileOutputStream;
   31   import java.io.RandomAccessFile;
   32   import java.io.IOException;
   33   import java.nio.ByteBuffer;
   34   import java.nio.MappedByteBuffer;
   35   import java.nio.channels;
   36   import java.nio.channels.spi;
   37   import java.util.ArrayList;
   38   import java.util.List;
   39   import java.util.Iterator;
   40   import java.util.concurrent.ConcurrentHashMap;
   41   import java.lang.ref.WeakReference;
   42   import java.lang.ref.ReferenceQueue;
   43   import java.lang.reflect.Field;
   44   import java.security.AccessController;
   45   import java.security.PrivilegedAction;
   46   import sun.misc.Cleaner;
   47   import sun.security.action.GetPropertyAction;
   48   
   49   
   50   public class FileChannelImpl
   51       extends FileChannel
   52   {
   53   
   54       // Used to make native read and write calls
   55       private static final NativeDispatcher nd;
   56   
   57       // Memory allocation size for mapping buffers
   58       private static final long allocationGranularity;
   59   
   60       // Cached field for MappedByteBuffer.isAMappedBuffer
   61       private static final Field isAMappedBufferField;
   62   
   63       // File descriptor
   64       private final FileDescriptor fd;
   65   
   66       // File access mode (immutable)
   67       private final boolean writable;
   68       private final boolean readable;
   69   
   70       // Required to prevent finalization of creating stream (immutable)
   71       private final Object parent;
   72   
   73       // Thread-safe set of IDs of native threads, for signalling
   74       private final NativeThreadSet threads = new NativeThreadSet(2);
   75   
   76       // Lock for operations involving position and size
   77       private final Object positionLock = new Object();
   78   
   79       private FileChannelImpl(FileDescriptor fd, boolean readable,
   80                               boolean writable, Object parent)
   81       {
   82           this.fd = fd;
   83           this.readable = readable;
   84           this.writable = writable;
   85           this.parent = parent;
   86       }
   87   
   88       // Invoked by getChannel() methods
   89       // of java.io.File{Input,Output}Stream and RandomAccessFile
   90       //
   91       public static FileChannel open(FileDescriptor fd,
   92                                      boolean readable, boolean writable,
   93                                      Object parent)
   94       {
   95           return new FileChannelImpl(fd, readable, writable, parent);
   96       }
   97   
   98       private void ensureOpen() throws IOException {
   99           if (!isOpen())
  100               throw new ClosedChannelException();
  101       }
  102   
  103   
  104       // -- Standard channel operations --
  105   
  106       protected void implCloseChannel() throws IOException {
  107   
  108           nd.preClose(fd);
  109           threads.signal();
  110   
  111           // Invalidate and release any locks that we still hold
  112           if (fileLockTable != null) {
  113               fileLockTable.removeAll( new FileLockTable.Releaser() {
  114                   public void release(FileLock fl) throws IOException {
  115                       ((FileLockImpl)fl).invalidate();
  116                       release0(fd, fl.position(), fl.size());
  117                   }
  118               });
  119           }
  120   
  121           if (parent != null) {
  122   
  123               // Close the fd via the parent stream's close method.  The parent
  124               // will reinvoke our close method, which is defined in the
  125               // superclass AbstractInterruptibleChannel, but the isOpen logic in
  126               // that method will prevent this method from being reinvoked.
  127               //
  128               ((java.io.Closeable)parent).close();
  129           } else {
  130               nd.close(fd);
  131           }
  132   
  133       }
  134   
  135       public int read(ByteBuffer dst) throws IOException {
  136           ensureOpen();
  137           if (!readable)
  138               throw new NonReadableChannelException();
  139           synchronized (positionLock) {
  140               int n = 0;
  141               int ti = -1;
  142               try {
  143                   begin();
  144                   if (!isOpen())
  145                       return 0;
  146                   ti = threads.add();
  147                   do {
  148                       n = IOUtil.read(fd, dst, -1, nd, positionLock);
  149                   } while ((n == IOStatus.INTERRUPTED) && isOpen());
  150                   return IOStatus.normalize(n);
  151               } finally {
  152                   threads.remove(ti);
  153                   end(n > 0);
  154                   assert IOStatus.check(n);
  155               }
  156           }
  157       }
  158   
  159       private long read0(ByteBuffer[] dsts) throws IOException {
  160           ensureOpen();
  161           if (!readable)
  162               throw new NonReadableChannelException();
  163           synchronized (positionLock) {
  164               long n = 0;
  165               int ti = -1;
  166               try {
  167                   begin();
  168                   if (!isOpen())
  169                       return 0;
  170                   ti = threads.add();
  171                   do {
  172                       n = IOUtil.read(fd, dsts, nd);
  173                   } while ((n == IOStatus.INTERRUPTED) && isOpen());
  174                   return IOStatus.normalize(n);
  175               } finally {
  176                   threads.remove(ti);
  177                   end(n > 0);
  178                   assert IOStatus.check(n);
  179               }
  180           }
  181       }
  182   
  183       public long read(ByteBuffer[] dsts, int offset, int length)
  184           throws IOException
  185       {
  186           if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
  187              throw new IndexOutOfBoundsException();
  188           // ## Fix IOUtil.write so that we can avoid this array copy
  189           return read0(Util.subsequence(dsts, offset, length));
  190       }
  191   
  192       public int write(ByteBuffer src) throws IOException {
  193           ensureOpen();
  194           if (!writable)
  195               throw new NonWritableChannelException();
  196           synchronized (positionLock) {
  197               int n = 0;
  198               int ti = -1;
  199               try {
  200                   begin();
  201                   if (!isOpen())
  202                       return 0;
  203                   ti = threads.add();
  204                   do {
  205                       n = IOUtil.write(fd, src, -1, nd, positionLock);
  206                   } while ((n == IOStatus.INTERRUPTED) && isOpen());
  207                   return IOStatus.normalize(n);
  208               } finally {
  209                   threads.remove(ti);
  210                   end(n > 0);
  211                   assert IOStatus.check(n);
  212               }
  213           }
  214       }
  215   
  216       private long write0(ByteBuffer[] srcs) throws IOException {
  217           ensureOpen();
  218           if (!writable)
  219               throw new NonWritableChannelException();
  220           synchronized (positionLock) {
  221               long n = 0;
  222               int ti = -1;
  223               try {
  224                   begin();
  225                   if (!isOpen())
  226                       return 0;
  227                   ti = threads.add();
  228                   do {
  229                       n = IOUtil.write(fd, srcs, nd);
  230                   } while ((n == IOStatus.INTERRUPTED) && isOpen());
  231                   return IOStatus.normalize(n);
  232               } finally {
  233                   threads.remove(ti);
  234                   end(n > 0);
  235                   assert IOStatus.check(n);
  236               }
  237           }
  238       }
  239   
  240       public long write(ByteBuffer[] srcs, int offset, int length)
  241           throws IOException
  242       {
  243           if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
  244              throw new IndexOutOfBoundsException();
  245           // ## Fix IOUtil.write so that we can avoid this array copy
  246           return write0(Util.subsequence(srcs, offset, length));
  247       }
  248   
  249   
  250       // -- Other operations --
  251   
  252       public long position() throws IOException {
  253           ensureOpen();
  254           synchronized (positionLock) {
  255               long p = -1;
  256               int ti = -1;
  257               try {
  258                   begin();
  259                   if (!isOpen())
  260                       return 0;
  261                   ti = threads.add();
  262                   do {
  263                       p = position0(fd, -1);
  264                   } while ((p == IOStatus.INTERRUPTED) && isOpen());
  265                   return IOStatus.normalize(p);
  266               } finally {
  267                   threads.remove(ti);
  268                   end(p > -1);
  269                   assert IOStatus.check(p);
  270               }
  271           }
  272       }
  273   
  274       public FileChannel position(long newPosition) throws IOException {
  275           ensureOpen();
  276           if (newPosition < 0)
  277               throw new IllegalArgumentException();
  278           synchronized (positionLock) {
  279               long p = -1;
  280               int ti = -1;
  281               try {
  282                   begin();
  283                   if (!isOpen())
  284                       return null;
  285                   ti = threads.add();
  286                   do {
  287                       p  = position0(fd, newPosition);
  288                   } while ((p == IOStatus.INTERRUPTED) && isOpen());
  289                   return this;
  290               } finally {
  291                   threads.remove(ti);
  292                   end(p > -1);
  293                   assert IOStatus.check(p);
  294               }
  295           }
  296       }
  297   
  298       public long size() throws IOException {
  299           ensureOpen();
  300           synchronized (positionLock) {
  301               long s = -1;
  302               int ti = -1;
  303               try {
  304                   begin();
  305                   if (!isOpen())
  306                       return -1;
  307                   ti = threads.add();
  308                   do {
  309                       s = size0(fd);
  310                   } while ((s == IOStatus.INTERRUPTED) && isOpen());
  311                   return IOStatus.normalize(s);
  312               } finally {
  313                   threads.remove(ti);
  314                   end(s > -1);
  315                   assert IOStatus.check(s);
  316               }
  317           }
  318       }
  319   
  320       public FileChannel truncate(long size) throws IOException {
  321           ensureOpen();
  322           if (size < 0)
  323               throw new IllegalArgumentException();
  324           if (size > size())
  325               return this;
  326           if (!writable)
  327               throw new NonWritableChannelException();
  328           synchronized (positionLock) {
  329               int rv = -1;
  330               long p = -1;
  331               int ti = -1;
  332               try {
  333                   begin();
  334                   if (!isOpen())
  335                       return null;
  336                   ti = threads.add();
  337   
  338                   // get current position
  339                   do {
  340                       p = position0(fd, -1);
  341                   } while ((p == IOStatus.INTERRUPTED) && isOpen());
  342                   if (!isOpen())
  343                       return null;
  344                   assert p >= 0;
  345   
  346                   // truncate file
  347                   do {
  348                       rv = truncate0(fd, size);
  349                   } while ((rv == IOStatus.INTERRUPTED) && isOpen());
  350                   if (!isOpen())
  351                       return null;
  352   
  353                   // set position to size if greater than size
  354                   if (p > size)
  355                       p = size;
  356                   do {
  357                       rv = (int)position0(fd, p);
  358                   } while ((rv == IOStatus.INTERRUPTED) && isOpen());
  359                   return this;
  360               } finally {
  361                   threads.remove(ti);
  362                   end(rv > -1);
  363                   assert IOStatus.check(rv);
  364               }
  365           }
  366       }
  367   
  368       public void force(boolean metaData) throws IOException {
  369           ensureOpen();
  370           int rv = -1;
  371           int ti = -1;
  372           try {
  373               begin();
  374               if (!isOpen())
  375                   return;
  376               ti = threads.add();
  377               do {
  378                   rv = force0(fd, metaData);
  379               } while ((rv == IOStatus.INTERRUPTED) && isOpen());
  380           } finally {
  381               threads.remove(ti);
  382               end(rv > -1);
  383               assert IOStatus.check(rv);
  384           }
  385       }
  386   
  387       // Assume at first that the underlying kernel supports sendfile();
  388       // set this to false if we find out later that it doesn't
  389       //
  390       private static volatile boolean transferSupported = true;
  391   
  392       // Assume that the underlying kernel sendfile() will work if the target
  393       // fd is a pipe; set this to false if we find out later that it doesn't
  394       //
  395       private static volatile boolean pipeSupported = true;
  396   
  397       // Assume that the underlying kernel sendfile() will work if the target
  398       // fd is a file; set this to false if we find out later that it doesn't
  399       //
  400       private static volatile boolean fileSupported = true;
  401   
  402       private long transferToDirectly(long position, int icount,
  403                                       WritableByteChannel target)
  404           throws IOException
  405       {
  406           if (!transferSupported)
  407               return IOStatus.UNSUPPORTED;
  408   
  409           FileDescriptor targetFD = null;
  410           if (target instanceof FileChannelImpl) {
  411               if (!fileSupported)
  412                   return IOStatus.UNSUPPORTED_CASE;
  413               targetFD = ((FileChannelImpl)target).fd;
  414           } else if (target instanceof SelChImpl) {
  415               // Direct transfer to pipe causes EINVAL on some configurations
  416               if ((target instanceof SinkChannelImpl) && !pipeSupported)
  417                   return IOStatus.UNSUPPORTED_CASE;
  418               targetFD = ((SelChImpl)target).getFD();
  419           }
  420           if (targetFD == null)
  421               return IOStatus.UNSUPPORTED;
  422           int thisFDVal = IOUtil.fdVal(fd);
  423           int targetFDVal = IOUtil.fdVal(targetFD);
  424           if (thisFDVal == targetFDVal) // Not supported on some configurations
  425               return IOStatus.UNSUPPORTED;
  426   
  427           long n = -1;
  428           int ti = -1;
  429           try {
  430               begin();
  431               if (!isOpen())
  432                   return -1;
  433               ti = threads.add();
  434               do {
  435                   n = transferTo0(thisFDVal, position, icount, targetFDVal);
  436               } while ((n == IOStatus.INTERRUPTED) && isOpen());
  437               if (n == IOStatus.UNSUPPORTED_CASE) {
  438                   if (target instanceof SinkChannelImpl)
  439                       pipeSupported = false;
  440                   if (target instanceof FileChannelImpl)
  441                       fileSupported = false;
  442                   return IOStatus.UNSUPPORTED_CASE;
  443               }
  444               if (n == IOStatus.UNSUPPORTED) {
  445                   // Don't bother trying again
  446                   transferSupported = false;
  447                   return IOStatus.UNSUPPORTED;
  448               }
  449               return IOStatus.normalize(n);
  450           } finally {
  451               threads.remove(ti);
  452               end (n > -1);
  453           }
  454       }
  455   
  456       private long transferToTrustedChannel(long position, int icount,
  457                                             WritableByteChannel target)
  458           throws IOException
  459       {
  460           if (  !((target instanceof FileChannelImpl)
  461                   || (target instanceof SelChImpl)))
  462               return IOStatus.UNSUPPORTED;
  463   
  464           // Trusted target: Use a mapped buffer
  465           MappedByteBuffer dbb = null;
  466           try {
  467               dbb = map(MapMode.READ_ONLY, position, icount);
  468               // ## Bug: Closing this channel will not terminate the write
  469               return target.write(dbb);
  470           } finally {
  471               if (dbb != null)
  472                   unmap(dbb);
  473           }
  474       }
  475   
  476       private long transferToArbitraryChannel(long position, int icount,
  477                                               WritableByteChannel target)
  478           throws IOException
  479       {
  480           // Untrusted target: Use a newly-erased buffer
  481           int c = Math.min(icount, TRANSFER_SIZE);
  482           ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
  483           long tw = 0;                    // Total bytes written
  484           long pos = position;
  485           try {
  486               Util.erase(bb);
  487               while (tw < icount) {
  488                   bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE));
  489                   int nr = read(bb, pos);
  490                   if (nr <= 0)
  491                       break;
  492                   bb.flip();
  493                   // ## Bug: Will block writing target if this channel
  494                   // ##      is asynchronously closed
  495                   int nw = target.write(bb);
  496                   tw += nw;
  497                   if (nw != nr)
  498                       break;
  499                   pos += nw;
  500                   bb.clear();
  501               }
  502               return tw;
  503           } catch (IOException x) {
  504               if (tw > 0)
  505                   return tw;
  506               throw x;
  507           } finally {
  508               Util.releaseTemporaryDirectBuffer(bb);
  509           }
  510       }
  511   
  512       public long transferTo(long position, long count,
  513                              WritableByteChannel target)
  514           throws IOException
  515       {
  516           ensureOpen();
  517           if (!target.isOpen())
  518               throw new ClosedChannelException();
  519           if (!readable)
  520               throw new NonReadableChannelException();
  521           if (target instanceof FileChannelImpl &&
  522               !((FileChannelImpl)target).writable)
  523               throw new NonWritableChannelException();
  524           if ((position < 0) || (count < 0))
  525               throw new IllegalArgumentException();
  526           long sz = size();
  527           if (position > sz)
  528               return 0;
  529           int icount = (int)Math.min(count, Integer.MAX_VALUE);
  530           if ((sz - position) < icount)
  531               icount = (int)(sz - position);
  532   
  533           long n;
  534   
  535           // Attempt a direct transfer, if the kernel supports it
  536           if ((n = transferToDirectly(position, icount, target)) >= 0)
  537               return n;
  538   
  539           // Attempt a mapped transfer, but only to trusted channel types
  540           if ((n = transferToTrustedChannel(position, icount, target)) >= 0)
  541               return n;
  542   
  543           // Slow path for untrusted targets
  544           return transferToArbitraryChannel(position, icount, target);
  545       }
  546   
  547       private long transferFromFileChannel(FileChannelImpl src,
  548                                            long position, long count)
  549           throws IOException
  550       {
  551           // Note we could loop here to accumulate more at once
  552           synchronized (src.positionLock) {
  553               long p = src.position();
  554               int icount = (int)Math.min(Math.min(count, Integer.MAX_VALUE),
  555                                          src.size() - p);
  556               // ## Bug: Closing this channel will not terminate the write
  557               MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, icount);
  558               try {
  559                   long n = write(bb, position);
  560                   src.position(p + n);
  561                   return n;
  562               } finally {
  563                   unmap(bb);
  564               }
  565           }
  566       }
  567   
  568       private static final int TRANSFER_SIZE = 8192;
  569   
  570       private long transferFromArbitraryChannel(ReadableByteChannel src,
  571                                                 long position, long count)
  572           throws IOException
  573       {
  574           // Untrusted target: Use a newly-erased buffer
  575           int c = (int)Math.min(count, TRANSFER_SIZE);
  576           ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
  577           long tw = 0;                    // Total bytes written
  578           long pos = position;
  579           try {
  580               Util.erase(bb);
  581               while (tw < count) {
  582                   bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
  583                   // ## Bug: Will block reading src if this channel
  584                   // ##      is asynchronously closed
  585                   int nr = src.read(bb);
  586                   if (nr <= 0)
  587                       break;
  588                   bb.flip();
  589                   int nw = write(bb, pos);
  590                   tw += nw;
  591                   if (nw != nr)
  592                       break;
  593                   pos += nw;
  594                   bb.clear();
  595               }
  596               return tw;
  597           } catch (IOException x) {
  598               if (tw > 0)
  599                   return tw;
  600               throw x;
  601           } finally {
  602               Util.releaseTemporaryDirectBuffer(bb);
  603           }
  604       }
  605   
  606       public long transferFrom(ReadableByteChannel src,
  607                                long position, long count)
  608           throws IOException
  609       {
  610           ensureOpen();
  611           if (!src.isOpen())
  612               throw new ClosedChannelException();
  613           if (!writable)
  614               throw new NonWritableChannelException();
  615           if ((position < 0) || (count < 0))
  616               throw new IllegalArgumentException();
  617           if (position > size())
  618               return 0;
  619           if (src instanceof FileChannelImpl)
  620              return transferFromFileChannel((FileChannelImpl)src,
  621                                             position, count);
  622   
  623           return transferFromArbitraryChannel(src, position, count);
  624       }
  625   
  626       public int read(ByteBuffer dst, long position) throws IOException {
  627           if (dst == null)
  628               throw new NullPointerException();
  629           if (position < 0)
  630               throw new IllegalArgumentException("Negative position");
  631           if (!readable)
  632               throw new NonReadableChannelException();
  633           ensureOpen();
  634           int n = 0;
  635           int ti = -1;
  636           try {
  637               begin();
  638               if (!isOpen())
  639                   return -1;
  640               ti = threads.add();
  641               do {
  642                   n = IOUtil.read(fd, dst, position, nd, positionLock);
  643               } while ((n == IOStatus.INTERRUPTED) && isOpen());
  644               return IOStatus.normalize(n);
  645           } finally {
  646               threads.remove(ti);
  647               end(n > 0);
  648               assert IOStatus.check(n);
  649           }
  650       }
  651   
  652       public int write(ByteBuffer src, long position) throws IOException {
  653           if (src == null)
  654               throw new NullPointerException();
  655           if (position < 0)
  656               throw new IllegalArgumentException("Negative position");
  657           if (!writable)
  658               throw new NonWritableChannelException();
  659           ensureOpen();
  660           int n = 0;
  661           int ti = -1;
  662           try {
  663               begin();
  664               if (!isOpen())
  665                   return -1;
  666               ti = threads.add();
  667               do {
  668                   n = IOUtil.write(fd, src, position, nd, positionLock);
  669               } while ((n == IOStatus.INTERRUPTED) && isOpen());
  670               return IOStatus.normalize(n);
  671           } finally {
  672               threads.remove(ti);
  673               end(n > 0);
  674               assert IOStatus.check(n);
  675           }
  676       }
  677   
  678   
  679       // -- Memory-mapped buffers --
  680   
  681       private static class Unmapper
  682           implements Runnable
  683       {
  684   
  685           private long address;
  686           private long size;
  687   
  688           private Unmapper(long address, long size) {
  689               assert (address != 0);
  690               this.address = address;
  691               this.size = size;
  692           }
  693   
  694           public void run() {
  695               if (address == 0)
  696                   return;
  697               unmap0(address, size);
  698               address = 0;
  699           }
  700   
  701       }
  702   
  703       private static void unmap(MappedByteBuffer bb) {
  704           Cleaner cl = ((DirectBuffer)bb).cleaner();
  705           if (cl != null)
  706               cl.clean();
  707       }
  708   
  709       private static final int MAP_RO = 0;
  710       private static final int MAP_RW = 1;
  711       private static final int MAP_PV = 2;
  712   
  713       public MappedByteBuffer map(MapMode mode, long position, long size)
  714           throws IOException
  715       {
  716           ensureOpen();
  717           if (position < 0L)
  718               throw new IllegalArgumentException("Negative position");
  719           if (size < 0L)
  720               throw new IllegalArgumentException("Negative size");
  721           if (position + size < 0)
  722               throw new IllegalArgumentException("Position + size overflow");
  723           if (size > Integer.MAX_VALUE)
  724               throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
  725           int imode = -1;
  726           if (mode == MapMode.READ_ONLY)
  727               imode = MAP_RO;
  728           else if (mode == MapMode.READ_WRITE)
  729               imode = MAP_RW;
  730           else if (mode == MapMode.PRIVATE)
  731               imode = MAP_PV;
  732           assert (imode >= 0);
  733           if ((mode != MapMode.READ_ONLY) && !writable)
  734               throw new NonWritableChannelException();
  735           if (!readable)
  736               throw new NonReadableChannelException();
  737   
  738           long addr = -1;
  739           int ti = -1;
  740           try {
  741               begin();
  742               if (!isOpen())
  743                   return null;
  744               ti = threads.add();
  745               if (size() < position + size) { // Extend file size
  746                   if (!writable) {
  747                       throw new IOException("Channel not open for writing " +
  748                           "- cannot extend file to required size");
  749                   }
  750                   int rv;
  751                   do {
  752                       rv = truncate0(fd, position + size);
  753                   } while ((rv == IOStatus.INTERRUPTED) && isOpen());
  754               }
  755               if (size == 0) {
  756                   addr = 0;
  757                   if ((!writable) || (imode == MAP_RO))
  758                       return Util.newMappedByteBufferR(0, 0, null);
  759                   else
  760                       return Util.newMappedByteBuffer(0, 0, null);
  761               }
  762   
  763               int pagePosition = (int)(position % allocationGranularity);
  764               long mapPosition = position - pagePosition;
  765               long mapSize = size + pagePosition;
  766               try {
  767                   // If no exception was thrown from map0, the address is valid
  768                   addr = map0(imode, mapPosition, mapSize);
  769               } catch (OutOfMemoryError x) {
  770                   // An OutOfMemoryError may indicate that we've exhausted memory
  771                   // so force gc and re-attempt map
  772                   System.gc();
  773                   try {
  774                       Thread.sleep(100);
  775                   } catch (InterruptedException y) {
  776                       Thread.currentThread().interrupt();
  777                   }
  778                   try {
  779                       addr = map0(imode, mapPosition, mapSize);
  780                   } catch (OutOfMemoryError y) {
  781                       // After a second OOME, fail
  782                       throw new IOException("Map failed", y);
  783                   }
  784               }
  785   
  786               assert (IOStatus.checkAll(addr));
  787               assert (addr % allocationGranularity == 0);
  788               int isize = (int)size;
  789               Unmapper um = new Unmapper(addr, size + pagePosition);
  790               if ((!writable) || (imode == MAP_RO))
  791                   return Util.newMappedByteBufferR(isize, addr + pagePosition, um);
  792               else
  793                   return Util.newMappedByteBuffer(isize, addr + pagePosition, um);
  794           } finally {
  795               threads.remove(ti);
  796               end(IOStatus.checkAll(addr));
  797           }
  798       }
  799   
  800   
  801       // -- Locks --
  802   
  803       public static final int NO_LOCK = -1;       // Failed to lock
  804       public static final int LOCKED = 0;         // Obtained requested lock
  805       public static final int RET_EX_LOCK = 1;    // Obtained exclusive lock
  806       public static final int INTERRUPTED = 2;    // Request interrupted
  807   
  808       // keeps track of locks on this file
  809       private volatile FileLockTable fileLockTable;
  810   
  811       // indicates if file locks are maintained system-wide (as per spec)
  812       private static boolean isSharedFileLockTable;
  813   
  814       // indicates if the disableSystemWideOverlappingFileLockCheck property
  815       // has been checked
  816       private static volatile boolean propertyChecked;
  817   
  818       // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
  819       // the overlap check wasn't system wide when there were multiple channels to
  820       // the same file. This property is used to get 1.4/5.0 behavior if desired.
  821       private static boolean isSharedFileLockTable() {
  822           if (!propertyChecked) {
  823               synchronized (FileChannelImpl.class) {
  824                   if (!propertyChecked) {
  825                       String value = AccessController.doPrivileged(
  826                           new GetPropertyAction(
  827                               "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
  828                       isSharedFileLockTable = ((value == null) || value.equals("false"));
  829                       propertyChecked = true;
  830                   }
  831               }
  832           }
  833           return isSharedFileLockTable;
  834       }
  835   
  836       private FileLockTable fileLockTable() {
  837           if (fileLockTable == null) {
  838               synchronized (this) {
  839                   if (fileLockTable == null) {
  840                       fileLockTable = isSharedFileLockTable() ?
  841                           new SharedFileLockTable(this) : new SimpleFileLockTable();
  842                   }
  843               }
  844           }
  845           return fileLockTable;
  846       }
  847   
  848       public FileLock lock(long position, long size, boolean shared)
  849           throws IOException
  850       {
  851           ensureOpen();
  852           if (shared && !readable)
  853               throw new NonReadableChannelException();
  854           if (!shared && !writable)
  855               throw new NonWritableChannelException();
  856           FileLockImpl fli = new FileLockImpl(this, position, size, shared);
  857           FileLockTable flt = fileLockTable();
  858           flt.add(fli);
  859           boolean i = true;
  860           int ti = -1;
  861           try {
  862               begin();
  863               if (!isOpen())
  864                   return null;
  865               ti = threads.add();
  866               int result = lock0(fd, true, position, size, shared);
  867               if (result == RET_EX_LOCK) {
  868                   assert shared;
  869                   FileLockImpl fli2 = new FileLockImpl(this, position, size,
  870                                                        false);
  871                   flt.replace(fli, fli2);
  872                   return fli2;
  873               }
  874               if (result == INTERRUPTED || result == NO_LOCK) {
  875                   flt.remove(fli);
  876                   i = false;
  877               }
  878           } catch (IOException e) {
  879               flt.remove(fli);
  880               throw e;
  881           } finally {
  882               threads.remove(ti);
  883               try {
  884                   end(i);
  885               } catch (ClosedByInterruptException e) {
  886                   throw new FileLockInterruptionException();
  887               }
  888           }
  889           return fli;
  890       }
  891   
  892       public FileLock tryLock(long position, long size, boolean shared)
  893           throws IOException
  894       {
  895           ensureOpen();
  896           if (shared && !readable)
  897               throw new NonReadableChannelException();
  898           if (!shared && !writable)
  899               throw new NonWritableChannelException();
  900           FileLockImpl fli = new FileLockImpl(this, position, size, shared);
  901           FileLockTable flt = fileLockTable();
  902           flt.add(fli);
  903           int result = lock0(fd, false, position, size, shared);
  904           if (result == NO_LOCK) {
  905               flt.remove(fli);
  906               return null;
  907           }
  908           if (result == RET_EX_LOCK) {
  909               assert shared;
  910               FileLockImpl fli2 = new FileLockImpl(this, position, size,
  911                                                    false);
  912               flt.replace(fli, fli2);
  913               return fli2;
  914           }
  915           return fli;
  916       }
  917   
  918       void release(FileLockImpl fli) throws IOException {
  919           ensureOpen();
  920           release0(fd, fli.position(), fli.size());
  921           assert fileLockTable != null;
  922           fileLockTable.remove(fli);
  923       }
  924   
  925   
  926       // -- File lock support  --
  927   
  928       /**
  929        * A table of FileLocks.
  930        */
  931       private interface FileLockTable {
  932           /**
  933            * Adds a file lock to the table.
  934            *
  935            * @throws OverlappingFileLockException if the file lock overlaps
  936            *         with an existing file lock in the table
  937            */
  938           void add(FileLock fl) throws OverlappingFileLockException;
  939   
  940           /**
  941            * Remove an existing file lock from the table.
  942            */
  943           void remove(FileLock fl);
  944   
  945           /**
  946            * An implementation of this interface releases a given file lock.
  947            * Used with removeAll.
  948            */
  949           interface Releaser {
  950               void release(FileLock fl) throws IOException;
  951           }
  952   
  953           /**
  954            * Removes all file locks from the table.
  955            * <p>
  956            * The Releaser#release method is invoked for each file lock before
  957            * it is removed.
  958            *
  959            * @throws IOException if the release method throws IOException
  960            */
  961           void removeAll(Releaser r) throws IOException;
  962   
  963           /**
  964            * Replaces an existing file lock in the table.
  965            */
  966           void replace(FileLock fl1, FileLock fl2);
  967       }
  968   
  969       /**
  970        * A simple file lock table that maintains a list of FileLocks obtained by a
  971        * FileChannel. Use to get 1.4/5.0 behaviour.
  972        */
  973       private static class SimpleFileLockTable implements FileLockTable {
  974           // synchronize on list for access
  975           private List<FileLock> lockList = new ArrayList<FileLock>(2);
  976   
  977           public SimpleFileLockTable() {
  978           }
  979   
  980           private void checkList(long position, long size)
  981               throws OverlappingFileLockException
  982           {
  983               assert Thread.holdsLock(lockList);
  984               for (FileLock fl: lockList) {
  985                   if (fl.overlaps(position, size)) {
  986                       throw new OverlappingFileLockException();
  987                   }
  988               }
  989           }
  990   
  991           public void add(FileLock fl) throws OverlappingFileLockException {
  992               synchronized (lockList) {
  993                   checkList(fl.position(), fl.size());
  994                   lockList.add(fl);
  995               }
  996           }
  997   
  998           public void remove(FileLock fl) {
  999               synchronized (lockList) {
 1000                   lockList.remove(fl);
 1001               }
 1002           }
 1003   
 1004           public void removeAll(Releaser releaser) throws IOException {
 1005               synchronized(lockList) {
 1006                   Iterator<FileLock> i = lockList.iterator();
 1007                   while (i.hasNext()) {
 1008                       FileLock fl = i.next();
 1009                       releaser.release(fl);
 1010                       i.remove();
 1011                   }
 1012               }
 1013           }
 1014   
 1015           public void replace(FileLock fl1, FileLock fl2) {
 1016               synchronized (lockList) {
 1017                   lockList.remove(fl1);
 1018                   lockList.add(fl2);
 1019               }
 1020           }
 1021       }
 1022   
 1023       /**
 1024        * A weak reference to a FileLock.
 1025        * <p>
 1026        * SharedFileLockTable uses a list of file lock references to avoid keeping the
 1027        * FileLock (and FileChannel) alive.
 1028        */
 1029       private static class FileLockReference extends WeakReference<FileLock> {
 1030           private FileKey fileKey;
 1031   
 1032           FileLockReference(FileLock referent,
 1033                             ReferenceQueue<FileLock> queue,
 1034                             FileKey key) {
 1035               super(referent, queue);
 1036               this.fileKey = key;
 1037           }
 1038   
 1039           private FileKey fileKey() {
 1040               return fileKey;
 1041           }
 1042       }
 1043   
 1044       /**
 1045        * A file lock table that is over a system-wide map of all file locks.
 1046        */
 1047       private static class SharedFileLockTable implements FileLockTable {
 1048           // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey.
 1049           // The map value is a list of file locks represented by FileLockReferences.
 1050           // All access to the list must be synchronized on the list.
 1051           private static ConcurrentHashMap<FileKey, ArrayList<FileLockReference>> lockMap =
 1052               new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
 1053   
 1054           // reference queue for cleared refs
 1055           private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
 1056   
 1057           // the enclosing file channel
 1058           private FileChannelImpl fci;
 1059   
 1060           // File key for the file that this channel is connected to
 1061           private FileKey fileKey;
 1062   
 1063           public SharedFileLockTable(FileChannelImpl fci) {
 1064               this.fci = fci;
 1065               this.fileKey = FileKey.create(fci.fd);
 1066           }
 1067   
 1068           public void add(FileLock fl) throws OverlappingFileLockException {
 1069               ArrayList<FileLockReference> list = lockMap.get(fileKey);
 1070   
 1071               for (;;) {
 1072   
 1073                   // The key isn't in the map so we try to create it atomically
 1074                   if (list == null) {
 1075                       list = new ArrayList<FileLockReference>(2);
 1076                       ArrayList<FileLockReference> prev;
 1077                       synchronized (list) {
 1078                           prev = lockMap.putIfAbsent(fileKey, list);
 1079                           if (prev == null) {
 1080                               // we successfully created the key so we add the file lock
 1081                               list.add(new FileLockReference(fl, queue, fileKey));
 1082                               break;
 1083                           }
 1084                       }
 1085                       // someone else got there first
 1086                       list = prev;
 1087                   }
 1088   
 1089                   // There is already a key. It is possible that some other thread
 1090                   // is removing it so we re-fetch the value from the map. If it
 1091                   // hasn't changed then we check the list for overlapping locks
 1092                   // and add the new lock to the list.
 1093                   synchronized (list) {
 1094                       ArrayList<FileLockReference> current = lockMap.get(fileKey);
 1095                       if (list == current) {
 1096                           checkList(list, fl.position(), fl.size());
 1097                           list.add(new FileLockReference(fl, queue, fileKey));
 1098                           break;
 1099                       }
 1100                       list = current;
 1101                   }
 1102   
 1103               }
 1104   
 1105               // process any stale entries pending in the reference queue
 1106               removeStaleEntries();
 1107           }
 1108   
 1109           private void removeKeyIfEmpty(FileKey fk, ArrayList<FileLockReference> list) {
 1110               assert Thread.holdsLock(list);
 1111               assert lockMap.get(fk) == list;
 1112               if (list.isEmpty()) {
 1113                   lockMap.remove(fk);
 1114               }
 1115           }
 1116   
 1117           public void remove(FileLock fl) {
 1118               assert fl != null;
 1119   
 1120               // the lock must exist so the list of locks must be present
 1121               ArrayList<FileLockReference> list = lockMap.get(fileKey);
 1122               assert list != null;
 1123   
 1124               synchronized (list) {
 1125                   int index = 0;
 1126                   while (index < list.size()) {
 1127                       FileLockReference ref = list.get(index);
 1128                       FileLock lock = ref.get();
 1129                       if (lock == fl) {
 1130                           assert (lock != null) && (lock.channel() == fci);
 1131                           ref.clear();
 1132                           list.remove(index);
 1133                           break;
 1134                       }
 1135                       index++;
 1136                   }
 1137               }
 1138           }
 1139   
 1140           public void removeAll(Releaser releaser) throws IOException {
 1141               ArrayList<FileLockReference> list = lockMap.get(fileKey);
 1142               if (list != null) {
 1143                   synchronized (list) {
 1144                       int index = 0;
 1145                       while (index < list.size()) {
 1146                           FileLockReference ref = list.get(index);
 1147                           FileLock lock = ref.get();
 1148   
 1149                           // remove locks obtained by this channel
 1150                           if (lock != null && lock.channel() == fci) {
 1151                               // invoke the releaser to invalidate/release the lock
 1152                               releaser.release(lock);
 1153   
 1154                               // remove the lock from the list
 1155                               ref.clear();
 1156                               list.remove(index);
 1157                           } else {
 1158                               index++;
 1159                           }
 1160                       }
 1161   
 1162                       // once the lock list is empty we remove it from the map
 1163                       removeKeyIfEmpty(fileKey, list);
 1164                   }
 1165               }
 1166           }
 1167   
 1168           public void replace(FileLock fromLock, FileLock toLock) {
 1169               // the lock must exist so there must be a list
 1170               ArrayList<FileLockReference> list = lockMap.get(fileKey);
 1171               assert list != null;
 1172   
 1173               synchronized (list) {
 1174                   for (int index=0; index<list.size(); index++) {
 1175                       FileLockReference ref = list.get(index);
 1176                       FileLock lock = ref.get();
 1177                       if (lock == fromLock) {
 1178                           ref.clear();
 1179                           list.set(index, new FileLockReference(toLock, queue, fileKey));
 1180                           break;
 1181                       }
 1182                   }
 1183               }
 1184           }
 1185   
 1186           // Check for overlapping file locks
 1187           private void checkList(List<FileLockReference> list, long position, long size)
 1188               throws OverlappingFileLockException
 1189           {
 1190               assert Thread.holdsLock(list);
 1191               for (FileLockReference ref: list) {
 1192                   FileLock fl = ref.get();
 1193                   if (fl != null && fl.overlaps(position, size))
 1194                       throw new OverlappingFileLockException();
 1195               }
 1196           }
 1197   
 1198           // Process the reference queue
 1199           private void removeStaleEntries() {
 1200               FileLockReference ref;
 1201               while ((ref = (FileLockReference)queue.poll()) != null) {
 1202                   FileKey fk = ref.fileKey();
 1203                   ArrayList<FileLockReference> list = lockMap.get(fk);
 1204                   if (list != null) {
 1205                       synchronized (list) {
 1206                           list.remove(ref);
 1207                           removeKeyIfEmpty(fk, list);
 1208                       }
 1209                   }
 1210               }
 1211           }
 1212       }
 1213   
 1214       // -- Native methods --
 1215   
 1216       // Grabs a file lock
 1217       native int lock0(FileDescriptor fd, boolean blocking, long pos, long size,
 1218                        boolean shared) throws IOException;
 1219   
 1220       // Releases a file lock
 1221       native void release0(FileDescriptor fd, long pos, long size)
 1222           throws IOException;
 1223   
 1224       // Creates a new mapping
 1225       private native long map0(int prot, long position, long length)
 1226           throws IOException;
 1227   
 1228       // Removes an existing mapping
 1229       private static native int unmap0(long address, long length);
 1230   
 1231       // Forces output to device
 1232       private native int force0(FileDescriptor fd, boolean metaData);
 1233   
 1234       // Truncates a file
 1235       private native int truncate0(FileDescriptor fd, long size);
 1236   
 1237       // Transfers from src to dst, or returns -2 if kernel can't do that
 1238       private native long transferTo0(int src, long position, long count, int dst);
 1239   
 1240       // Sets or reports this file's position
 1241       // If offset is -1, the current position is returned
 1242       // otherwise the position is set to offset
 1243       private native long position0(FileDescriptor fd, long offset);
 1244   
 1245       // Reports this file's size
 1246       private native long size0(FileDescriptor fd);
 1247   
 1248       // Caches fieldIDs
 1249       private static native long initIDs();
 1250   
 1251       static {
 1252           Util.load();
 1253           allocationGranularity = initIDs();
 1254           nd = new FileDispatcher();
 1255           isAMappedBufferField = Reflect.lookupField("java.nio.MappedByteBuffer",
 1256                                             "isAMappedBuffer");
 1257       }
 1258   
 1259   }

Save This Page
Home » openjdk-7 » sun » nio » ch » [javadoc | source]