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