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