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