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