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