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