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