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