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