1 /*
   2  * Copyright 2000-2009 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.IOException;
  30 import java.nio.ByteBuffer;
  31 import java.nio.MappedByteBuffer;
  32 import java.nio.BufferPoolMXBean;
  33 import java.nio.channels.*;
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 import java.util.Iterator;
  37 import java.lang.reflect.Field;
  38 import java.security.AccessController;
  39 import javax.management.ObjectName;
  40 import javax.management.MalformedObjectNameException;
  41 import sun.misc.Cleaner;
  42 import sun.security.action.GetPropertyAction;
  43 
  44 public class FileChannelImpl
  45     extends FileChannel
  46 {
  47 
  48     // Used to make native read and write calls
  49     private static final FileDispatcher nd;
  50 
  51     // Memory allocation size for mapping buffers
  52     private static final long allocationGranularity;
  53 
  54     // File descriptor
  55     private final FileDescriptor fd;
  56 
  57     // File access mode (immutable)
  58     private final boolean writable;
  59     private final boolean readable;
  60 
  61     // Required to prevent finalization of creating stream (immutable)
  62     private final Object parent;
  63 
  64     // Thread-safe set of IDs of native threads, for signalling
  65     private final NativeThreadSet threads = new NativeThreadSet(2);
  66 
  67     // Lock for operations involving position and size
  68     private final Object positionLock = new Object();
  69 
  70     private FileChannelImpl(FileDescriptor fd, boolean readable,
  71                             boolean writable, Object parent)
  72     {
  73         this.fd = fd;
  74         this.readable = readable;
  75         this.writable = writable;
  76         this.parent = parent;
  77     }
  78 
  79     // Invoked by getChannel() methods
  80     // of java.io.File{Input,Output}Stream and RandomAccessFile
  81     //
  82     public static FileChannel open(FileDescriptor fd,
  83                                    boolean readable, boolean writable,
  84                                    Object parent)
  85     {
  86         return new FileChannelImpl(fd, readable, writable, parent);
  87     }
  88 
  89     private void ensureOpen() throws IOException {
  90         if (!isOpen())
  91             throw new ClosedChannelException();
  92     }
  93 
  94 
  95     // -- Standard channel operations --
  96 
  97     protected void implCloseChannel() throws IOException {
  98         // Invalidate and release any locks that we still hold
  99         if (fileLockTable != null) {
 100             fileLockTable.removeAll( new FileLockTable.Releaser() {
 101                 public void release(FileLock fl) throws IOException {
 102                     ((FileLockImpl)fl).invalidate();
 103                     nd.release(fd, fl.position(), fl.size());
 104                 }
 105             });
 106         }
 107 
 108         nd.preClose(fd);
 109         threads.signalAndWait();
 110 
 111         if (parent != null) {
 112 
 113             // Close the fd via the parent stream's close method.  The parent
 114             // will reinvoke our close method, which is defined in the
 115             // superclass AbstractInterruptibleChannel, but the isOpen logic in
 116             // that method will prevent this method from being reinvoked.
 117             //
 118             ((java.io.Closeable)parent).close();
 119         } else {
 120             nd.close(fd);
 121         }
 122 
 123     }
 124 
 125     public int read(ByteBuffer dst) throws IOException {
 126         ensureOpen();
 127         if (!readable)
 128             throw new NonReadableChannelException();
 129         synchronized (positionLock) {
 130             int n = 0;
 131             int ti = -1;
 132             try {
 133                 begin();
 134                 ti = threads.add();
 135                 if (!isOpen())
 136                     return 0;
 137                 do {
 138                     n = IOUtil.read(fd, dst, -1, nd, positionLock);
 139                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 140                 return IOStatus.normalize(n);
 141             } finally {
 142                 threads.remove(ti);
 143                 end(n > 0);
 144                 assert IOStatus.check(n);
 145             }
 146         }
 147     }
 148 
 149     private long read0(ByteBuffer[] dsts) throws IOException {
 150         ensureOpen();
 151         if (!readable)
 152             throw new NonReadableChannelException();
 153         synchronized (positionLock) {
 154             long n = 0;
 155             int ti = -1;
 156             try {
 157                 begin();
 158                 ti = threads.add();
 159                 if (!isOpen())
 160                     return 0;
 161                 do {
 162                     n = IOUtil.read(fd, dsts, nd);
 163                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 164                 return IOStatus.normalize(n);
 165             } finally {
 166                 threads.remove(ti);
 167                 end(n > 0);
 168                 assert IOStatus.check(n);
 169             }
 170         }
 171     }
 172 
 173     public long read(ByteBuffer[] dsts, int offset, int length)
 174         throws IOException
 175     {
 176         if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
 177            throw new IndexOutOfBoundsException();
 178         // ## Fix IOUtil.write so that we can avoid this array copy
 179         return read0(Util.subsequence(dsts, offset, length));
 180     }
 181 
 182     public int write(ByteBuffer src) throws IOException {
 183         ensureOpen();
 184         if (!writable)
 185             throw new NonWritableChannelException();
 186         synchronized (positionLock) {
 187             int n = 0;
 188             int ti = -1;
 189             try {
 190                 begin();
 191                 ti = threads.add();
 192                 if (!isOpen())
 193                     return 0;
 194                 do {
 195                     n = IOUtil.write(fd, src, -1, nd, positionLock);
 196                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 197                 return IOStatus.normalize(n);
 198             } finally {
 199                 threads.remove(ti);
 200                 end(n > 0);
 201                 assert IOStatus.check(n);
 202             }
 203         }
 204     }
 205 
 206     private long write0(ByteBuffer[] srcs) throws IOException {
 207         ensureOpen();
 208         if (!writable)
 209             throw new NonWritableChannelException();
 210         synchronized (positionLock) {
 211             long n = 0;
 212             int ti = -1;
 213             try {
 214                 begin();
 215                 ti = threads.add();
 216                 if (!isOpen())
 217                     return 0;
 218                 do {
 219                     n = IOUtil.write(fd, srcs, nd);
 220                 } while ((n == IOStatus.INTERRUPTED) && isOpen());
 221                 return IOStatus.normalize(n);
 222             } finally {
 223                 threads.remove(ti);
 224                 end(n > 0);
 225                 assert IOStatus.check(n);
 226             }
 227         }
 228     }
 229 
 230     public long write(ByteBuffer[] srcs, int offset, int length)
 231         throws IOException
 232     {
 233         if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
 234            throw new IndexOutOfBoundsException();
 235         // ## Fix IOUtil.write so that we can avoid this array copy
 236         return write0(Util.subsequence(srcs, offset, length));
 237     }
 238 
 239 
 240     // -- Other operations --
 241 
 242     public long position() throws IOException {
 243         ensureOpen();
 244         synchronized (positionLock) {
 245             long p = -1;
 246             int ti = -1;
 247             try {
 248                 begin();
 249                 ti = threads.add();
 250                 if (!isOpen())
 251                     return 0;
 252                 do {
 253                     p = position0(fd, -1);
 254                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
 255                 return IOStatus.normalize(p);
 256             } finally {
 257                 threads.remove(ti);
 258                 end(p > -1);
 259                 assert IOStatus.check(p);
 260             }
 261         }
 262     }
 263 
 264     public FileChannel position(long newPosition) throws IOException {
 265         ensureOpen();
 266         if (newPosition < 0)
 267             throw new IllegalArgumentException();
 268         synchronized (positionLock) {
 269             long p = -1;
 270             int ti = -1;
 271             try {
 272                 begin();
 273                 ti = threads.add();
 274                 if (!isOpen())
 275                     return null;
 276                 do {
 277                     p  = position0(fd, newPosition);
 278                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
 279                 return this;
 280             } finally {
 281                 threads.remove(ti);
 282                 end(p > -1);
 283                 assert IOStatus.check(p);
 284             }
 285         }
 286     }
 287 
 288     public long size() throws IOException {
 289         ensureOpen();
 290         synchronized (positionLock) {
 291             long s = -1;
 292             int ti = -1;
 293             try {
 294                 begin();
 295                 ti = threads.add();
 296                 if (!isOpen())
 297                     return -1;
 298                 do {
 299                     s = nd.size(fd);
 300                 } while ((s == IOStatus.INTERRUPTED) && isOpen());
 301                 return IOStatus.normalize(s);
 302             } finally {
 303                 threads.remove(ti);
 304                 end(s > -1);
 305                 assert IOStatus.check(s);
 306             }
 307         }
 308     }
 309 
 310     public FileChannel truncate(long size) throws IOException {
 311         ensureOpen();
 312         if (size < 0)
 313             throw new IllegalArgumentException();
 314         if (size > size())
 315             return this;
 316         if (!writable)
 317             throw new NonWritableChannelException();
 318         synchronized (positionLock) {
 319             int rv = -1;
 320             long p = -1;
 321             int ti = -1;
 322             try {
 323                 begin();
 324                 ti = threads.add();
 325                 if (!isOpen())
 326                     return null;
 327 
 328                 // get current position
 329                 do {
 330                     p = position0(fd, -1);
 331                 } while ((p == IOStatus.INTERRUPTED) && isOpen());
 332                 if (!isOpen())
 333                     return null;
 334                 assert p >= 0;
 335 
 336                 // truncate file
 337                 do {
 338                     rv = nd.truncate(fd, size);
 339                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
 340                 if (!isOpen())
 341                     return null;
 342 
 343                 // set position to size if greater than size
 344                 if (p > size)
 345                     p = size;
 346                 do {
 347                     rv = (int)position0(fd, p);
 348                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
 349                 return this;
 350             } finally {
 351                 threads.remove(ti);
 352                 end(rv > -1);
 353                 assert IOStatus.check(rv);
 354             }
 355         }
 356     }
 357 
 358     public void force(boolean metaData) throws IOException {
 359         ensureOpen();
 360         int rv = -1;
 361         int ti = -1;
 362         try {
 363             begin();
 364             ti = threads.add();
 365             if (!isOpen())
 366                 return;
 367             do {
 368                 rv = nd.force(fd, metaData);
 369             } while ((rv == IOStatus.INTERRUPTED) && isOpen());
 370         } finally {
 371             threads.remove(ti);
 372             end(rv > -1);
 373             assert IOStatus.check(rv);
 374         }
 375     }
 376 
 377     // Assume at first that the underlying kernel supports sendfile();
 378     // set this to false if we find out later that it doesn't
 379     //
 380     private static volatile boolean transferSupported = true;
 381 
 382     // Assume that the underlying kernel sendfile() will work if the target
 383     // fd is a pipe; set this to false if we find out later that it doesn't
 384     //
 385     private static volatile boolean pipeSupported = true;
 386 
 387     // Assume that the underlying kernel sendfile() will work if the target
 388     // fd is a file; set this to false if we find out later that it doesn't
 389     //
 390     private static volatile boolean fileSupported = true;
 391 
 392     private long transferToDirectly(long position, int icount,
 393                                     WritableByteChannel target)
 394         throws IOException
 395     {
 396         if (!transferSupported)
 397             return IOStatus.UNSUPPORTED;
 398 
 399         FileDescriptor targetFD = null;
 400         if (target instanceof FileChannelImpl) {
 401             if (!fileSupported)
 402                 return IOStatus.UNSUPPORTED_CASE;
 403             targetFD = ((FileChannelImpl)target).fd;
 404         } else if (target instanceof SelChImpl) {
 405             // Direct transfer to pipe causes EINVAL on some configurations
 406             if ((target instanceof SinkChannelImpl) && !pipeSupported)
 407                 return IOStatus.UNSUPPORTED_CASE;
 408             targetFD = ((SelChImpl)target).getFD();
 409         }
 410         if (targetFD == null)
 411             return IOStatus.UNSUPPORTED;
 412         int thisFDVal = IOUtil.fdVal(fd);
 413         int targetFDVal = IOUtil.fdVal(targetFD);
 414         if (thisFDVal == targetFDVal) // Not supported on some configurations
 415             return IOStatus.UNSUPPORTED;
 416 
 417         long n = -1;
 418         int ti = -1;
 419         try {
 420             begin();
 421             ti = threads.add();
 422             if (!isOpen())
 423                 return -1;
 424             do {
 425                 n = transferTo0(thisFDVal, position, icount, targetFDVal);
 426             } while ((n == IOStatus.INTERRUPTED) && isOpen());
 427             if (n == IOStatus.UNSUPPORTED_CASE) {
 428                 if (target instanceof SinkChannelImpl)
 429                     pipeSupported = false;
 430                 if (target instanceof FileChannelImpl)
 431                     fileSupported = false;
 432                 return IOStatus.UNSUPPORTED_CASE;
 433             }
 434             if (n == IOStatus.UNSUPPORTED) {
 435                 // Don't bother trying again
 436                 transferSupported = false;
 437                 return IOStatus.UNSUPPORTED;
 438             }
 439             return IOStatus.normalize(n);
 440         } finally {
 441             threads.remove(ti);
 442             end (n > -1);
 443         }
 444     }
 445 
 446     private long transferToTrustedChannel(long position, int icount,
 447                                           WritableByteChannel target)
 448         throws IOException
 449     {
 450         if (  !((target instanceof FileChannelImpl)
 451                 || (target instanceof SelChImpl)))
 452             return IOStatus.UNSUPPORTED;
 453 
 454         // Trusted target: Use a mapped buffer
 455         MappedByteBuffer dbb = null;
 456         try {
 457             dbb = map(MapMode.READ_ONLY, position, icount);
 458             // ## Bug: Closing this channel will not terminate the write
 459             return target.write(dbb);
 460         } finally {
 461             if (dbb != null)
 462                 unmap(dbb);
 463         }
 464     }
 465 
 466     private long transferToArbitraryChannel(long position, int icount,
 467                                             WritableByteChannel target)
 468         throws IOException
 469     {
 470         // Untrusted target: Use a newly-erased buffer
 471         int c = Math.min(icount, TRANSFER_SIZE);
 472         ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
 473         long tw = 0;                    // Total bytes written
 474         long pos = position;
 475         try {
 476             Util.erase(bb);
 477             while (tw < icount) {
 478                 bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE));
 479                 int nr = read(bb, pos);
 480                 if (nr <= 0)
 481                     break;
 482                 bb.flip();
 483                 // ## Bug: Will block writing target if this channel
 484                 // ##      is asynchronously closed
 485                 int nw = target.write(bb);
 486                 tw += nw;
 487                 if (nw != nr)
 488                     break;
 489                 pos += nw;
 490                 bb.clear();
 491             }
 492             return tw;
 493         } catch (IOException x) {
 494             if (tw > 0)
 495                 return tw;
 496             throw x;
 497         } finally {
 498             Util.releaseTemporaryDirectBuffer(bb);
 499         }
 500     }
 501 
 502     public long transferTo(long position, long count,
 503                            WritableByteChannel target)
 504         throws IOException
 505     {
 506         ensureOpen();
 507         if (!target.isOpen())
 508             throw new ClosedChannelException();
 509         if (!readable)
 510             throw new NonReadableChannelException();
 511         if (target instanceof FileChannelImpl &&
 512             !((FileChannelImpl)target).writable)
 513             throw new NonWritableChannelException();
 514         if ((position < 0) || (count < 0))
 515             throw new IllegalArgumentException();
 516         long sz = size();
 517         if (position > sz)
 518             return 0;
 519         int icount = (int)Math.min(count, Integer.MAX_VALUE);
 520         if ((sz - position) < icount)
 521             icount = (int)(sz - position);
 522 
 523         long n;
 524 
 525         // Attempt a direct transfer, if the kernel supports it
 526         if ((n = transferToDirectly(position, icount, target)) >= 0)
 527             return n;
 528 
 529         // Attempt a mapped transfer, but only to trusted channel types
 530         if ((n = transferToTrustedChannel(position, icount, target)) >= 0)
 531             return n;
 532 
 533         // Slow path for untrusted targets
 534         return transferToArbitraryChannel(position, icount, target);
 535     }
 536 
 537     private long transferFromFileChannel(FileChannelImpl src,
 538                                          long position, long count)
 539         throws IOException
 540     {
 541         // Note we could loop here to accumulate more at once
 542         synchronized (src.positionLock) {
 543             long p = src.position();
 544             int icount = (int)Math.min(Math.min(count, Integer.MAX_VALUE),
 545                                        src.size() - p);
 546             // ## Bug: Closing this channel will not terminate the write
 547             MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, icount);
 548             try {
 549                 long n = write(bb, position);
 550                 src.position(p + n);
 551                 return n;
 552             } finally {
 553                 unmap(bb);
 554             }
 555         }
 556     }
 557 
 558     private static final int TRANSFER_SIZE = 8192;
 559 
 560     private long transferFromArbitraryChannel(ReadableByteChannel src,
 561                                               long position, long count)
 562         throws IOException
 563     {
 564         // Untrusted target: Use a newly-erased buffer
 565         int c = (int)Math.min(count, TRANSFER_SIZE);
 566         ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
 567         long tw = 0;                    // Total bytes written
 568         long pos = position;
 569         try {
 570             Util.erase(bb);
 571             while (tw < count) {
 572                 bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
 573                 // ## Bug: Will block reading src if this channel
 574                 // ##      is asynchronously closed
 575                 int nr = src.read(bb);
 576                 if (nr <= 0)
 577                     break;
 578                 bb.flip();
 579                 int nw = write(bb, pos);
 580                 tw += nw;
 581                 if (nw != nr)
 582                     break;
 583                 pos += nw;
 584                 bb.clear();
 585             }
 586             return tw;
 587         } catch (IOException x) {
 588             if (tw > 0)
 589                 return tw;
 590             throw x;
 591         } finally {
 592             Util.releaseTemporaryDirectBuffer(bb);
 593         }
 594     }
 595 
 596     public long transferFrom(ReadableByteChannel src,
 597                              long position, long count)
 598         throws IOException
 599     {
 600         ensureOpen();
 601         if (!src.isOpen())
 602             throw new ClosedChannelException();
 603         if (!writable)
 604             throw new NonWritableChannelException();
 605         if ((position < 0) || (count < 0))
 606             throw new IllegalArgumentException();
 607         if (position > size())
 608             return 0;
 609         if (src instanceof FileChannelImpl)
 610            return transferFromFileChannel((FileChannelImpl)src,
 611                                           position, count);
 612 
 613         return transferFromArbitraryChannel(src, position, count);
 614     }
 615 
 616     public int read(ByteBuffer dst, long position) throws IOException {
 617         if (dst == null)
 618             throw new NullPointerException();
 619         if (position < 0)
 620             throw new IllegalArgumentException("Negative position");
 621         if (!readable)
 622             throw new NonReadableChannelException();
 623         ensureOpen();
 624         int n = 0;
 625         int ti = -1;
 626         try {
 627             begin();
 628             ti = threads.add();
 629             if (!isOpen())
 630                 return -1;
 631             do {
 632                 n = IOUtil.read(fd, dst, position, nd, positionLock);
 633             } while ((n == IOStatus.INTERRUPTED) && isOpen());
 634             return IOStatus.normalize(n);
 635         } finally {
 636             threads.remove(ti);
 637             end(n > 0);
 638             assert IOStatus.check(n);
 639         }
 640     }
 641 
 642     public int write(ByteBuffer src, long position) throws IOException {
 643         if (src == null)
 644             throw new NullPointerException();
 645         if (position < 0)
 646             throw new IllegalArgumentException("Negative position");
 647         if (!writable)
 648             throw new NonWritableChannelException();
 649         ensureOpen();
 650         int n = 0;
 651         int ti = -1;
 652         try {
 653             begin();
 654             ti = threads.add();
 655             if (!isOpen())
 656                 return -1;
 657             do {
 658                 n = IOUtil.write(fd, src, position, nd, positionLock);
 659             } while ((n == IOStatus.INTERRUPTED) && isOpen());
 660             return IOStatus.normalize(n);
 661         } finally {
 662             threads.remove(ti);
 663             end(n > 0);
 664             assert IOStatus.check(n);
 665         }
 666     }
 667 
 668 
 669     // -- Memory-mapped buffers --
 670 
 671     private static class Unmapper
 672         implements Runnable
 673     {
 674         // keep track of mapped buffer usage
 675         static volatile int count;
 676         static volatile long totalSize;
 677         static volatile long totalCapacity;
 678 
 679         private long address;
 680         private long size;
 681         private int cap;
 682 
 683         private Unmapper(long address, long size, int cap) {
 684             assert (address != 0);
 685             this.address = address;
 686             this.size = size;
 687             this.cap = cap;
 688 
 689             synchronized (Unmapper.class) {
 690                 count++;
 691                 totalSize += size;
 692                 totalCapacity += cap;
 693             }
 694         }
 695 
 696         public void run() {
 697             if (address == 0)
 698                 return;
 699             unmap0(address, size);
 700             address = 0;
 701 
 702             synchronized (Unmapper.class) {
 703                 count--;
 704                 totalSize -= size;
 705                 totalCapacity -= cap;
 706             }
 707         }
 708     }
 709 
 710     private static void unmap(MappedByteBuffer bb) {
 711         Cleaner cl = ((DirectBuffer)bb).cleaner();
 712         if (cl != null)
 713             cl.clean();
 714     }
 715 
 716     private static final int MAP_RO = 0;
 717     private static final int MAP_RW = 1;
 718     private static final int MAP_PV = 2;
 719 
 720     public MappedByteBuffer map(MapMode mode, long position, long size)
 721         throws IOException
 722     {
 723         ensureOpen();
 724         if (position < 0L)
 725             throw new IllegalArgumentException("Negative position");
 726         if (size < 0L)
 727             throw new IllegalArgumentException("Negative size");
 728         if (position + size < 0)
 729             throw new IllegalArgumentException("Position + size overflow");
 730         if (size > Integer.MAX_VALUE)
 731             throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
 732         int imode = -1;
 733         if (mode == MapMode.READ_ONLY)
 734             imode = MAP_RO;
 735         else if (mode == MapMode.READ_WRITE)
 736             imode = MAP_RW;
 737         else if (mode == MapMode.PRIVATE)
 738             imode = MAP_PV;
 739         assert (imode >= 0);
 740         if ((mode != MapMode.READ_ONLY) && !writable)
 741             throw new NonWritableChannelException();
 742         if (!readable)
 743             throw new NonReadableChannelException();
 744 
 745         long addr = -1;
 746         int ti = -1;
 747         try {
 748             begin();
 749             ti = threads.add();
 750             if (!isOpen())
 751                 return null;
 752             if (size() < position + size) { // Extend file size
 753                 if (!writable) {
 754                     throw new IOException("Channel not open for writing " +
 755                         "- cannot extend file to required size");
 756                 }
 757                 int rv;
 758                 do {
 759                     rv = nd.truncate(fd, position + size);
 760                 } while ((rv == IOStatus.INTERRUPTED) && isOpen());
 761             }
 762             if (size == 0) {
 763                 addr = 0;
 764                 if ((!writable) || (imode == MAP_RO))
 765                     return Util.newMappedByteBufferR(0, 0, null);
 766                 else
 767                     return Util.newMappedByteBuffer(0, 0, null);
 768             }
 769 
 770             int pagePosition = (int)(position % allocationGranularity);
 771             long mapPosition = position - pagePosition;
 772             long mapSize = size + pagePosition;
 773             try {
 774                 // If no exception was thrown from map0, the address is valid
 775                 addr = map0(imode, mapPosition, mapSize);
 776             } catch (OutOfMemoryError x) {
 777                 // An OutOfMemoryError may indicate that we've exhausted memory
 778                 // so force gc and re-attempt map
 779                 System.gc();
 780                 try {
 781                     Thread.sleep(100);
 782                 } catch (InterruptedException y) {
 783                     Thread.currentThread().interrupt();
 784                 }
 785                 try {
 786                     addr = map0(imode, mapPosition, mapSize);
 787                 } catch (OutOfMemoryError y) {
 788                     // After a second OOME, fail
 789                     throw new IOException("Map failed", y);
 790                 }
 791             }
 792 
 793             assert (IOStatus.checkAll(addr));
 794             assert (addr % allocationGranularity == 0);
 795             int isize = (int)size;
 796             Unmapper um = new Unmapper(addr, size + pagePosition, isize);
 797             if ((!writable) || (imode == MAP_RO))
 798                 return Util.newMappedByteBufferR(isize, addr + pagePosition, um);
 799             else
 800                 return Util.newMappedByteBuffer(isize, addr + pagePosition, um);
 801         } finally {
 802             threads.remove(ti);
 803             end(IOStatus.checkAll(addr));
 804         }
 805     }
 806 
 807     /**
 808      * Returns the management interface for mapped buffers
 809      */
 810     public static BufferPoolMXBean getMappedBufferPoolMXBean() {
 811         return LazyInitialization.mappedBufferPoolMXBean;
 812     }
 813 
 814     // Lazy initialization of management interface
 815     private static class LazyInitialization {
 816         static final BufferPoolMXBean mappedBufferPoolMXBean = mappedBufferPoolMXBean();
 817 
 818         private static BufferPoolMXBean mappedBufferPoolMXBean() {
 819             final String pool = "mapped";
 820             final ObjectName obj;
 821             try {
 822                 obj = new ObjectName("java.nio:type=BufferPool,name=" + pool);
 823             } catch (MalformedObjectNameException x) {
 824                 throw new AssertionError(x);
 825             }
 826             return new BufferPoolMXBean() {
 827                 @Override
 828                 public ObjectName getObjectName() {
 829                     return obj;
 830                 }
 831                 @Override
 832                 public String getName() {
 833                     return pool;
 834                 }
 835                 @Override
 836                 public long getCount() {
 837                     return Unmapper.count;
 838                 }
 839                 @Override
 840                 public long getTotalCapacity() {
 841                     return Unmapper.totalCapacity;
 842                 }
 843                 @Override
 844                 public long getMemoryUsed() {
 845                     return Unmapper.totalSize;
 846                 }
 847             };
 848         }
 849     }
 850 
 851     // -- Locks --
 852 
 853 
 854 
 855     // keeps track of locks on this file
 856     private volatile FileLockTable fileLockTable;
 857 
 858     // indicates if file locks are maintained system-wide (as per spec)
 859     private static boolean isSharedFileLockTable;
 860 
 861     // indicates if the disableSystemWideOverlappingFileLockCheck property
 862     // has been checked
 863     private static volatile boolean propertyChecked;
 864 
 865     // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so
 866     // the overlap check wasn't system wide when there were multiple channels to
 867     // the same file. This property is used to get 1.4/5.0 behavior if desired.
 868     private static boolean isSharedFileLockTable() {
 869         if (!propertyChecked) {
 870             synchronized (FileChannelImpl.class) {
 871                 if (!propertyChecked) {
 872                     String value = AccessController.doPrivileged(
 873                         new GetPropertyAction(
 874                             "sun.nio.ch.disableSystemWideOverlappingFileLockCheck"));
 875                     isSharedFileLockTable = ((value == null) || value.equals("false"));
 876                     propertyChecked = true;
 877                 }
 878             }
 879         }
 880         return isSharedFileLockTable;
 881     }
 882 
 883     private FileLockTable fileLockTable() throws IOException {
 884         if (fileLockTable == null) {
 885             synchronized (this) {
 886                 if (fileLockTable == null) {
 887                     if (isSharedFileLockTable()) {
 888                         int ti = threads.add();
 889                         try {
 890                             ensureOpen();
 891                             fileLockTable = FileLockTable.newSharedFileLockTable(this, fd);
 892                         } finally {
 893                             threads.remove(ti);
 894                         }
 895                     } else {
 896                         fileLockTable = new SimpleFileLockTable();
 897                     }
 898                 }
 899             }
 900         }
 901         return fileLockTable;
 902     }
 903 
 904     public FileLock lock(long position, long size, boolean shared)
 905         throws IOException
 906     {
 907         ensureOpen();
 908         if (shared && !readable)
 909             throw new NonReadableChannelException();
 910         if (!shared && !writable)
 911             throw new NonWritableChannelException();
 912         FileLockImpl fli = new FileLockImpl(this, position, size, shared);
 913         FileLockTable flt = fileLockTable();
 914         flt.add(fli);
 915         boolean i = true;
 916         int ti = -1;
 917         try {
 918             begin();
 919             ti = threads.add();
 920             if (!isOpen())
 921                 return null;
 922             int result = nd.lock(fd, true, position, size, shared);
 923             if (result == FileDispatcher.RET_EX_LOCK) {
 924                 assert shared;
 925                 FileLockImpl fli2 = new FileLockImpl(this, position, size,
 926                                                      false);
 927                 flt.replace(fli, fli2);
 928                 return fli2;
 929             }
 930             if (result == FileDispatcher.INTERRUPTED || result == FileDispatcher.NO_LOCK) {
 931                 flt.remove(fli);
 932                 i = false;
 933             }
 934         } catch (IOException e) {
 935             flt.remove(fli);
 936             throw e;
 937         } finally {
 938             threads.remove(ti);
 939             try {
 940                 end(i);
 941             } catch (ClosedByInterruptException e) {
 942                 throw new FileLockInterruptionException();
 943             }
 944         }
 945         return fli;
 946     }
 947 
 948     public FileLock tryLock(long position, long size, boolean shared)
 949         throws IOException
 950     {
 951         ensureOpen();
 952         if (shared && !readable)
 953             throw new NonReadableChannelException();
 954         if (!shared && !writable)
 955             throw new NonWritableChannelException();
 956         FileLockImpl fli = new FileLockImpl(this, position, size, shared);
 957         FileLockTable flt = fileLockTable();
 958         flt.add(fli);
 959         int result;
 960 
 961         int ti = threads.add();
 962         try {
 963             try {
 964                 ensureOpen();
 965                 result = nd.lock(fd, false, position, size, shared);
 966             } catch (IOException e) {
 967                 flt.remove(fli);
 968                 throw e;
 969             }
 970             if (result == FileDispatcher.NO_LOCK) {
 971                 flt.remove(fli);
 972                 return null;
 973             }
 974             if (result == FileDispatcher.RET_EX_LOCK) {
 975                 assert shared;
 976                 FileLockImpl fli2 = new FileLockImpl(this, position, size,
 977                                                      false);
 978                 flt.replace(fli, fli2);
 979                 return fli2;
 980             }
 981             return fli;
 982         } finally {
 983             threads.remove(ti);
 984         }
 985     }
 986 
 987     void release(FileLockImpl fli) throws IOException {
 988         ensureOpen();
 989         int ti = threads.add();
 990         try {
 991             ensureOpen();
 992             nd.release(fd, fli.position(), fli.size());
 993         } finally {
 994             threads.remove(ti);
 995         }
 996         assert fileLockTable != null;
 997         fileLockTable.remove(fli);
 998     }
 999 
1000     // -- File lock support --
1001 
1002     /**
1003      * A simple file lock table that maintains a list of FileLocks obtained by a
1004      * FileChannel. Use to get 1.4/5.0 behaviour.
1005      */
1006     private static class SimpleFileLockTable extends FileLockTable {
1007         // synchronize on list for access
1008         private List<FileLock> lockList = new ArrayList<FileLock>(2);
1009 
1010         public SimpleFileLockTable() {
1011         }
1012 
1013         private void checkList(long position, long size)
1014             throws OverlappingFileLockException
1015         {
1016             assert Thread.holdsLock(lockList);
1017             for (FileLock fl: lockList) {
1018                 if (fl.overlaps(position, size)) {
1019                     throw new OverlappingFileLockException();
1020                 }
1021             }
1022         }
1023 
1024         public void add(FileLock fl) throws OverlappingFileLockException {
1025             synchronized (lockList) {
1026                 checkList(fl.position(), fl.size());
1027                 lockList.add(fl);
1028             }
1029         }
1030 
1031         public void remove(FileLock fl) {
1032             synchronized (lockList) {
1033                 lockList.remove(fl);
1034             }
1035         }
1036 
1037         public void removeAll(Releaser releaser) throws IOException {
1038             synchronized(lockList) {
1039                 Iterator<FileLock> i = lockList.iterator();
1040                 while (i.hasNext()) {
1041                     FileLock fl = i.next();
1042                     releaser.release(fl);
1043                     i.remove();
1044                 }
1045             }
1046         }
1047 
1048         public void replace(FileLock fl1, FileLock fl2) {
1049             synchronized (lockList) {
1050                 lockList.remove(fl1);
1051                 lockList.add(fl2);
1052             }
1053         }
1054     }
1055 
1056     // -- Native methods --
1057 
1058     // Creates a new mapping
1059     private native long map0(int prot, long position, long length)
1060         throws IOException;
1061 
1062     // Removes an existing mapping
1063     private static native int unmap0(long address, long length);
1064 
1065     // Transfers from src to dst, or returns -2 if kernel can't do that
1066     private native long transferTo0(int src, long position, long count, int dst);
1067 
1068     // Sets or reports this file's position
1069     // If offset is -1, the current position is returned
1070     // otherwise the position is set to offset
1071     private native long position0(FileDescriptor fd, long offset);
1072 
1073     // Caches fieldIDs
1074     private static native long initIDs();
1075 
1076     static {
1077         Util.load();
1078         allocationGranularity = initIDs();
1079         nd = new FileDispatcherImpl();
1080     }
1081 
1082 }