1 /* 2 * Copyright (c) 2000, 2024, 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.Closeable; 29 import java.io.FileDescriptor; 30 import java.io.IOException; 31 import java.io.UncheckedIOException; 32 import java.lang.foreign.MemorySegment; 33 import java.lang.foreign.Arena; 34 import java.lang.ref.Cleaner.Cleanable; 35 import java.nio.ByteBuffer; 36 import java.nio.MappedByteBuffer; 37 import java.nio.channels.AsynchronousCloseException; 38 import java.nio.channels.Channel; 39 import java.nio.channels.ClosedByInterruptException; 40 import java.nio.channels.ClosedChannelException; 41 import java.nio.channels.FileChannel; 42 import java.nio.channels.FileLock; 43 import java.nio.channels.FileLockInterruptionException; 44 import java.nio.channels.NonReadableChannelException; 45 import java.nio.channels.NonWritableChannelException; 46 import java.nio.channels.ReadableByteChannel; 47 import java.nio.channels.SelectableChannel; 48 import java.nio.channels.WritableByteChannel; 49 import java.util.Objects; 50 51 import jdk.internal.access.JavaIOFileDescriptorAccess; 52 import jdk.internal.access.SharedSecrets; 53 import jdk.internal.foreign.MemorySessionImpl; 54 import jdk.internal.foreign.SegmentFactories; 55 import jdk.internal.misc.Blocker; 56 import jdk.internal.misc.ExtendedMapMode; 57 import jdk.internal.misc.Unsafe; 58 import jdk.internal.misc.VM; 59 import jdk.internal.misc.VM.BufferPool; 60 import jdk.internal.ref.Cleaner; 61 import jdk.internal.ref.CleanerFactory; 62 63 import jdk.internal.access.foreign.UnmapperProxy; 64 65 public class FileChannelImpl 66 extends FileChannel 67 { 68 // Access to FileDescriptor internals 69 private static final JavaIOFileDescriptorAccess fdAccess = 70 SharedSecrets.getJavaIOFileDescriptorAccess(); 71 72 // Used to make native read and write calls 73 private static final FileDispatcher nd = new FileDispatcherImpl(); 74 75 // File descriptor 76 private final FileDescriptor fd; 77 78 // File access mode (immutable) 79 private final boolean writable; 80 private final boolean readable; 81 82 // Required to prevent finalization of creating stream (immutable) 83 private final Closeable parent; 84 85 // The path of the referenced file 86 // (null if the parent stream is created with a file descriptor) 87 private final String path; 88 89 // Thread-safe set of IDs of native threads, for signalling 90 private final NativeThreadSet threads = new NativeThreadSet(2); 91 92 // Lock for operations involving position and size 93 private final Object positionLock = new Object(); 94 95 // blocking operations are not interruptible 96 private volatile boolean uninterruptible; 97 98 // DirectIO flag 99 private final boolean direct; 100 101 // IO alignment value for DirectIO 102 private final int alignment; 103 104 // Cleanable with an action which closes this channel's file descriptor 105 private final Cleanable closer; 106 107 private static class Closer implements Runnable { 108 private final FileDescriptor fd; 109 110 Closer(FileDescriptor fd) { 111 this.fd = fd; 112 } 113 114 public void run() { 115 try { 116 fdAccess.close(fd); 117 } catch (IOException ioe) { 118 // Rethrow as unchecked so the exception can be propagated as needed 119 throw new UncheckedIOException("close", ioe); 120 } 121 } 122 } 123 124 private FileChannelImpl(FileDescriptor fd, String path, boolean readable, 125 boolean writable, boolean direct, Closeable parent) 126 { 127 this.fd = fd; 128 this.path = path; 129 this.readable = readable; 130 this.writable = writable; 131 this.direct = direct; 132 this.parent = parent; 133 if (direct) { 134 assert path != null; 135 this.alignment = nd.setDirectIO(fd, path); 136 } else { 137 this.alignment = -1; 138 } 139 140 // Register a cleaning action if and only if there is no parent 141 // as the parent will take care of closing the file descriptor. 142 // FileChannel is used by the LambdaMetaFactory so a lambda cannot 143 // be used here hence we use a nested class instead. 144 this.closer = parent != null ? null : 145 CleanerFactory.cleaner().register(this, new Closer(fd)); 146 } 147 148 149 // Used by FileInputStream::getChannel, FileOutputStream::getChannel, 150 // and RandomAccessFile::getChannel 151 public static FileChannel open(FileDescriptor fd, String path, 152 boolean readable, boolean writable, 153 boolean direct, Closeable parent) 154 { 155 return new FileChannelImpl(fd, path, readable, writable, direct, parent); 156 } 157 158 private void ensureOpen() throws IOException { 159 if (!isOpen()) 160 throw new ClosedChannelException(); 161 } 162 163 public void setUninterruptible() { 164 uninterruptible = true; 165 } 166 167 private void beginBlocking() { 168 if (!uninterruptible) begin(); 169 } 170 171 private void endBlocking(boolean completed) throws AsynchronousCloseException { 172 if (!uninterruptible) end(completed); 173 } 174 175 // -- Standard channel operations -- 176 177 protected void implCloseChannel() throws IOException { 178 if (!fd.valid()) 179 return; // nothing to do 180 181 // Release and invalidate any locks that we still hold 182 if (fileLockTable != null) { 183 for (FileLock fl: fileLockTable.removeAll()) { 184 synchronized (fl) { 185 if (fl.isValid()) { 186 nd.release(fd, fl.position(), fl.size()); 187 ((FileLockImpl)fl).invalidate(); 188 } 189 } 190 } 191 } 192 193 // signal any threads blocked on this channel 194 threads.signalAndWait(); 195 196 if (parent != null) { 197 // 198 // Close the fd via the parent stream's close method. The parent 199 // will reinvoke our close method, which is defined in the 200 // superclass AbstractInterruptibleChannel, but the isOpen logic in 201 // that method will prevent this method from being reinvoked. 202 // 203 parent.close(); 204 } else { // parent == null hence closer != null 205 // 206 // Perform the cleaning action so it is not redone when 207 // this channel becomes phantom reachable. 208 // 209 try { 210 closer.clean(); 211 } catch (UncheckedIOException uioe) { 212 throw uioe.getCause(); 213 } 214 } 215 } 216 217 @Override 218 public int read(ByteBuffer dst) throws IOException { 219 ensureOpen(); 220 if (!readable) 221 throw new NonReadableChannelException(); 222 synchronized (positionLock) { 223 if (direct) 224 Util.checkChannelPositionAligned(position(), alignment); 225 int n = 0; 226 int ti = -1; 227 try { 228 beginBlocking(); 229 ti = threads.add(); 230 if (!isOpen()) 231 return 0; 232 do { 233 long comp = Blocker.begin(); 234 try { 235 n = IOUtil.read(fd, dst, -1, direct, alignment, nd); 236 } finally { 237 Blocker.end(comp); 238 } 239 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 240 return IOStatus.normalize(n); 241 } finally { 242 threads.remove(ti); 243 endBlocking(n > 0); 244 assert IOStatus.check(n); 245 } 246 } 247 } 248 249 @Override 250 public long read(ByteBuffer[] dsts, int offset, int length) 251 throws IOException 252 { 253 Objects.checkFromIndexSize(offset, length, dsts.length); 254 ensureOpen(); 255 if (!readable) 256 throw new NonReadableChannelException(); 257 synchronized (positionLock) { 258 if (direct) 259 Util.checkChannelPositionAligned(position(), alignment); 260 long n = 0; 261 int ti = -1; 262 try { 263 beginBlocking(); 264 ti = threads.add(); 265 if (!isOpen()) 266 return 0; 267 do { 268 long comp = Blocker.begin(); 269 try { 270 n = IOUtil.read(fd, dsts, offset, length, direct, alignment, nd); 271 } finally { 272 Blocker.end(comp); 273 } 274 275 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 276 return IOStatus.normalize(n); 277 } finally { 278 threads.remove(ti); 279 endBlocking(n > 0); 280 assert IOStatus.check(n); 281 } 282 } 283 } 284 285 @Override 286 public int write(ByteBuffer src) throws IOException { 287 ensureOpen(); 288 if (!writable) 289 throw new NonWritableChannelException(); 290 synchronized (positionLock) { 291 if (direct) 292 Util.checkChannelPositionAligned(position(), alignment); 293 int n = 0; 294 int ti = -1; 295 try { 296 beginBlocking(); 297 ti = threads.add(); 298 if (!isOpen()) 299 return 0; 300 do { 301 long comp = Blocker.begin(); 302 try { 303 n = IOUtil.write(fd, src, -1, direct, alignment, nd); 304 } finally { 305 Blocker.end(comp); 306 } 307 308 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 309 return IOStatus.normalize(n); 310 } finally { 311 threads.remove(ti); 312 endBlocking(n > 0); 313 assert IOStatus.check(n); 314 } 315 } 316 } 317 318 @Override 319 public long write(ByteBuffer[] srcs, int offset, int length) 320 throws IOException 321 { 322 Objects.checkFromIndexSize(offset, length, srcs.length); 323 ensureOpen(); 324 if (!writable) 325 throw new NonWritableChannelException(); 326 synchronized (positionLock) { 327 if (direct) 328 Util.checkChannelPositionAligned(position(), alignment); 329 long n = 0; 330 int ti = -1; 331 try { 332 beginBlocking(); 333 ti = threads.add(); 334 if (!isOpen()) 335 return 0; 336 do { 337 long comp = Blocker.begin(); 338 try { 339 n = IOUtil.write(fd, srcs, offset, length, direct, alignment, nd); 340 } finally { 341 Blocker.end(comp); 342 } 343 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 344 return IOStatus.normalize(n); 345 } finally { 346 threads.remove(ti); 347 endBlocking(n > 0); 348 assert IOStatus.check(n); 349 } 350 } 351 } 352 353 // -- Other operations -- 354 355 @Override 356 public long position() throws IOException { 357 ensureOpen(); 358 synchronized (positionLock) { 359 long p = -1; 360 int ti = -1; 361 try { 362 beginBlocking(); 363 ti = threads.add(); 364 if (!isOpen()) 365 return 0; 366 boolean append = fdAccess.getAppend(fd); 367 do { 368 long comp = Blocker.begin(); 369 try { 370 // in append-mode then position is advanced to end before writing 371 p = (append) ? nd.size(fd) : nd.seek(fd, -1); 372 } finally { 373 Blocker.end(comp); 374 } 375 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 376 return IOStatus.normalize(p); 377 } finally { 378 threads.remove(ti); 379 endBlocking(p > -1); 380 assert IOStatus.check(p); 381 } 382 } 383 } 384 385 @Override 386 public FileChannel position(long newPosition) throws IOException { 387 ensureOpen(); 388 if (newPosition < 0) 389 throw new IllegalArgumentException(); 390 synchronized (positionLock) { 391 long p = -1; 392 int ti = -1; 393 try { 394 beginBlocking(); 395 ti = threads.add(); 396 if (!isOpen()) 397 return null; 398 do { 399 long comp = Blocker.begin(); 400 try { 401 p = nd.seek(fd, newPosition); 402 } finally { 403 Blocker.end(comp); 404 } 405 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 406 return this; 407 } finally { 408 threads.remove(ti); 409 endBlocking(p > -1); 410 assert IOStatus.check(p); 411 } 412 } 413 } 414 415 @Override 416 public long size() throws IOException { 417 ensureOpen(); 418 synchronized (positionLock) { 419 long s = -1; 420 int ti = -1; 421 try { 422 beginBlocking(); 423 ti = threads.add(); 424 if (!isOpen()) 425 return -1; 426 do { 427 long comp = Blocker.begin(); 428 try { 429 s = nd.size(fd); 430 } finally { 431 Blocker.end(comp); 432 } 433 } while ((s == IOStatus.INTERRUPTED) && isOpen()); 434 return IOStatus.normalize(s); 435 } finally { 436 threads.remove(ti); 437 endBlocking(s > -1); 438 assert IOStatus.check(s); 439 } 440 } 441 } 442 443 @Override 444 public FileChannel truncate(long newSize) throws IOException { 445 ensureOpen(); 446 if (newSize < 0) 447 throw new IllegalArgumentException("Negative size"); 448 if (!writable) 449 throw new NonWritableChannelException(); 450 synchronized (positionLock) { 451 int rv = -1; 452 long p = -1; 453 int ti = -1; 454 long rp = -1; 455 try { 456 beginBlocking(); 457 ti = threads.add(); 458 if (!isOpen()) 459 return null; 460 461 // get current size 462 long size; 463 do { 464 long comp = Blocker.begin(); 465 try { 466 size = nd.size(fd); 467 } finally { 468 Blocker.end(comp); 469 } 470 } while ((size == IOStatus.INTERRUPTED) && isOpen()); 471 if (!isOpen()) 472 return null; 473 474 // get current position 475 do { 476 long comp = Blocker.begin(); 477 try { 478 p = nd.seek(fd, -1); 479 } finally { 480 Blocker.end(comp); 481 } 482 } while ((p == IOStatus.INTERRUPTED) && isOpen()); 483 if (!isOpen()) 484 return null; 485 assert p >= 0; 486 487 // truncate file if given size is less than the current size 488 if (newSize < size) { 489 do { 490 long comp = Blocker.begin(); 491 try { 492 rv = nd.truncate(fd, newSize); 493 } finally { 494 Blocker.end(comp); 495 } 496 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 497 if (!isOpen()) 498 return null; 499 } 500 501 // if position is beyond new size then adjust it 502 if (p > newSize) 503 p = newSize; 504 do { 505 long comp = Blocker.begin(); 506 try { 507 rp = nd.seek(fd, p); 508 } finally { 509 Blocker.end(comp); 510 } 511 } while ((rp == IOStatus.INTERRUPTED) && isOpen()); 512 return this; 513 } finally { 514 threads.remove(ti); 515 endBlocking(rv > -1); 516 assert IOStatus.check(rv); 517 } 518 } 519 } 520 521 @Override 522 public void force(boolean metaData) throws IOException { 523 ensureOpen(); 524 int rv = -1; 525 int ti = -1; 526 try { 527 beginBlocking(); 528 ti = threads.add(); 529 if (!isOpen()) 530 return; 531 do { 532 long comp = Blocker.begin(); 533 try { 534 rv = nd.force(fd, metaData); 535 } finally { 536 Blocker.end(comp); 537 } 538 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 539 } finally { 540 threads.remove(ti); 541 endBlocking(rv > -1); 542 assert IOStatus.check(rv); 543 } 544 } 545 546 // Assume at first that the underlying kernel supports sendfile/equivalent; 547 // set this to true if we find out later that it doesn't 548 // 549 private static volatile boolean transferToDirectNotSupported; 550 551 // Assume at first that the underlying kernel supports copy_file_range/equivalent; 552 // set this to true if we find out later that it doesn't 553 // 554 private static volatile boolean transferFromDirectNotSupported; 555 556 /** 557 * Marks the beginning of a transfer to or from this channel. 558 * @throws ClosedChannelException if channel is closed 559 */ 560 private int beforeTransfer() throws ClosedChannelException { 561 int ti = threads.add(); 562 if (isOpen()) { 563 return ti; 564 } else { 565 threads.remove(ti); 566 throw new ClosedChannelException(); 567 } 568 } 569 570 /** 571 * Marks the end of a transfer to or from this channel. 572 * @throws AsynchronousCloseException if not completed and the channel is closed 573 */ 574 private void afterTransfer(boolean completed, int ti) throws AsynchronousCloseException { 575 threads.remove(ti); 576 if (!completed && !isOpen()) { 577 throw new AsynchronousCloseException(); 578 } 579 } 580 581 /** 582 * Invoked when ClosedChannelException is thrown during a transfer. This method 583 * translates it to an AsynchronousCloseException or ClosedByInterruptException. In 584 * the case of ClosedByInterruptException, it ensures that this channel and the 585 * source/target channel are closed. 586 */ 587 private AsynchronousCloseException transferFailed(ClosedChannelException cce, Channel other) { 588 ClosedByInterruptException cbie = null; 589 if (cce instanceof ClosedByInterruptException e) { 590 assert Thread.currentThread().isInterrupted(); 591 cbie = e; 592 } else if (!uninterruptible && Thread.currentThread().isInterrupted()) { 593 cbie = new ClosedByInterruptException(); 594 } 595 if (cbie != null) { 596 try { 597 this.close(); 598 } catch (IOException ioe) { 599 cbie.addSuppressed(ioe); 600 } 601 try { 602 other.close(); 603 } catch (IOException ioe) { 604 cbie.addSuppressed(ioe); 605 } 606 return cbie; 607 608 } 609 // one of the channels was closed during the transfer 610 if (cce instanceof AsynchronousCloseException ace) { 611 return ace; 612 } else { 613 var ace = new AsynchronousCloseException(); 614 ace.addSuppressed(cce); 615 return ace; 616 } 617 } 618 619 /** 620 * Transfers bytes from this channel's file to the resource that the given file 621 * descriptor is connected to. 622 */ 623 private long transferToFileDescriptor(long position, int count, FileDescriptor targetFD) { 624 long n; 625 boolean append = fdAccess.getAppend(targetFD); 626 do { 627 long comp = Blocker.begin(); 628 try { 629 n = nd.transferTo(fd, position, count, targetFD, append); 630 } finally { 631 Blocker.end(comp); 632 } 633 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 634 return n; 635 } 636 637 /** 638 * Transfers bytes from this channel's file to the given channel's file. 639 */ 640 private long transferToFileChannel(long position, int count, FileChannelImpl target) 641 throws IOException 642 { 643 final FileChannelImpl source = this; 644 boolean completed = false; 645 try { 646 beginBlocking(); 647 int sourceIndex = source.beforeTransfer(); 648 try { 649 int targetIndex = target.beforeTransfer(); 650 try { 651 long n = transferToFileDescriptor(position, count, target.fd); 652 completed = (n >= 0); 653 return IOStatus.normalize(n); 654 } finally { 655 target.afterTransfer(completed, targetIndex); 656 } 657 } finally { 658 source.afterTransfer(completed, sourceIndex); 659 } 660 } finally { 661 endBlocking(completed); 662 } 663 } 664 665 /** 666 * Transfers bytes from this channel's file to the given channel's socket. 667 */ 668 private long transferToSocketChannel(long position, int count, SocketChannelImpl target) 669 throws IOException 670 { 671 final FileChannelImpl source = this; 672 boolean completed = false; 673 try { 674 beginBlocking(); 675 int sourceIndex = source.beforeTransfer(); 676 try { 677 target.beforeTransferTo(); 678 try { 679 long n = transferToFileDescriptor(position, count, target.getFD()); 680 completed = (n >= 0); 681 return IOStatus.normalize(n); 682 } finally { 683 target.afterTransferTo(completed); 684 } 685 } finally { 686 source.afterTransfer(completed, sourceIndex); 687 } 688 } finally { 689 endBlocking(completed); 690 } 691 } 692 693 /** 694 * Transfers bytes from this channel's file from the given channel's file. This 695 * implementation uses sendfile, copy_file_range or equivalent. 696 */ 697 private long transferToDirectInternal(long position, int count, WritableByteChannel target) 698 throws IOException 699 { 700 assert !nd.transferToDirectlyNeedsPositionLock() || Thread.holdsLock(positionLock); 701 702 return switch (target) { 703 case FileChannelImpl fci -> transferToFileChannel(position, count, fci); 704 case SocketChannelImpl sci -> transferToSocketChannel(position, count, sci); 705 default -> IOStatus.UNSUPPORTED_CASE; 706 }; 707 } 708 709 /** 710 * Transfers bytes from this channel's file to the given channel's file or socket. 711 * @return the number of bytes transferred, UNSUPPORTED_CASE if this transfer cannot 712 * be done directly, or UNSUPPORTED if there is no direct support 713 */ 714 private long transferToDirect(long position, int count, WritableByteChannel target) 715 throws IOException 716 { 717 if (transferToDirectNotSupported) 718 return IOStatus.UNSUPPORTED; 719 if (target instanceof SelectableChannel sc && !nd.canTransferToDirectly(sc)) 720 return IOStatus.UNSUPPORTED_CASE; 721 722 long n; 723 if (nd.transferToDirectlyNeedsPositionLock()) { 724 synchronized (positionLock) { 725 long pos = position(); 726 try { 727 n = transferToDirectInternal(position, count, target); 728 } finally { 729 try { 730 position(pos); 731 } catch (ClosedChannelException ignore) { 732 // can't reset position if channel is closed 733 } 734 } 735 } 736 } else { 737 n = transferToDirectInternal(position, count, target); 738 } 739 if (n == IOStatus.UNSUPPORTED) { 740 transferToDirectNotSupported = true; 741 } 742 return n; 743 } 744 745 // Size threshold above which to use a mapped buffer; 746 // transferToArbitraryChannel() and transferFromArbitraryChannel() 747 // are faster for smaller transfers 748 private static final long MAPPED_TRANSFER_THRESHOLD = 16L*1024L; 749 750 // Maximum size to map when using a mapped buffer 751 private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L; 752 753 /** 754 * Transfers bytes from channel's file to the given channel. This implementation 755 * memory maps this channel's file. 756 */ 757 private long transferToTrustedChannel(long position, long count, 758 WritableByteChannel target) 759 throws IOException 760 { 761 if (count < MAPPED_TRANSFER_THRESHOLD) 762 return IOStatus.UNSUPPORTED_CASE; 763 764 boolean isSelChImpl = (target instanceof SelChImpl); 765 if (!((target instanceof FileChannelImpl) || isSelChImpl)) 766 return IOStatus.UNSUPPORTED_CASE; 767 768 if (target == this) { 769 long posThis = position(); 770 if ((posThis - count + 1 <= position) 771 && (position - count + 1 <= posThis) 772 && !nd.canTransferToFromOverlappedMap()) { 773 return IOStatus.UNSUPPORTED_CASE; 774 } 775 } 776 777 long remaining = count; 778 while (remaining > 0L) { 779 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); 780 try { 781 MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size); 782 try { 783 // write may block, closing this channel will not wake it up 784 int n = target.write(dbb); 785 assert n >= 0; 786 remaining -= n; 787 if (isSelChImpl) { 788 // one attempt to write to selectable channel 789 break; 790 } 791 assert n > 0; 792 position += n; 793 } finally { 794 unmap(dbb); 795 } 796 } catch (IOException ioe) { 797 // Only throw exception if no bytes have been written 798 if (remaining == count) 799 throw ioe; 800 break; 801 } 802 } 803 return count - remaining; 804 } 805 806 /** 807 * Transfers bytes from channel's file to the given channel. 808 */ 809 private long transferToArbitraryChannel(long position, long count, 810 WritableByteChannel target) 811 throws IOException 812 { 813 // Untrusted target: Use a newly-erased buffer 814 int c = (int) Math.min(count, TRANSFER_SIZE); 815 ByteBuffer bb = ByteBuffer.allocate(c); 816 long tw = 0; // Total bytes written 817 long pos = position; 818 try { 819 while (tw < count) { 820 bb.limit((int) Math.min(count - tw, TRANSFER_SIZE)); 821 int nr = read(bb, pos); 822 if (nr <= 0) 823 break; 824 bb.flip(); 825 // write may block, closing this channel will not wake it up 826 int nw = target.write(bb); 827 tw += nw; 828 if (nw != nr) 829 break; 830 pos += nw; 831 bb.clear(); 832 } 833 return tw; 834 } catch (IOException x) { 835 if (tw > 0) 836 return tw; 837 throw x; 838 } 839 } 840 841 @Override 842 public long transferTo(long position, long count, WritableByteChannel target) 843 throws IOException 844 { 845 ensureOpen(); 846 if (!target.isOpen()) 847 throw new ClosedChannelException(); 848 if (!readable) 849 throw new NonReadableChannelException(); 850 if (target instanceof FileChannelImpl && !((FileChannelImpl) target).writable) 851 throw new NonWritableChannelException(); 852 if ((position < 0) || (count < 0)) 853 throw new IllegalArgumentException(); 854 855 try { 856 final long sz = size(); 857 if (position > sz) 858 return 0; 859 860 // System calls supporting fast transfers might not work on files 861 // which advertise zero size such as those in Linux /proc 862 if (sz > 0) { 863 // Now sz > 0 and position <= sz so remaining >= 0 and 864 // remaining == 0 if and only if sz == position 865 long remaining = sz - position; 866 867 if (remaining >= 0 && remaining < count) 868 count = remaining; 869 870 // Attempt a direct transfer, if the kernel supports it, 871 // limiting the number of bytes according to which platform 872 int icount = (int) Math.min(count, nd.maxDirectTransferSize()); 873 long n; 874 if ((n = transferToDirect(position, icount, target)) >= 0) 875 return n; 876 877 // Attempt a mapped transfer, but only to trusted channel types 878 if ((n = transferToTrustedChannel(position, count, target)) >= 0) 879 return n; 880 } 881 882 // fallback to read/write loop 883 return transferToArbitraryChannel(position, count, target); 884 } catch (ClosedChannelException e) { 885 // throw AsynchronousCloseException or ClosedByInterruptException 886 throw transferFailed(e, target); 887 } 888 } 889 890 /** 891 * Transfers bytes into this channel's file from the resource that the given file 892 * descriptor is connected to. 893 */ 894 private long transferFromFileDescriptor(FileDescriptor srcFD, long position, long count) { 895 long n; 896 boolean append = fdAccess.getAppend(fd); 897 do { 898 long comp = Blocker.begin(); 899 try { 900 n = nd.transferFrom(srcFD, fd, position, count, append); 901 } finally { 902 Blocker.end(comp); 903 } 904 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 905 return n; 906 } 907 908 /** 909 * Transfers bytes into this channel's file from the given channel's file. This 910 * implementation uses copy_file_range or equivalent. 911 */ 912 private long transferFromDirect(FileChannelImpl src, long position, long count) 913 throws IOException 914 { 915 if (transferFromDirectNotSupported) 916 return IOStatus.UNSUPPORTED; 917 918 final FileChannelImpl target = this; 919 boolean completed = false; 920 try { 921 beginBlocking(); 922 int srcIndex = src.beforeTransfer(); 923 try { 924 int targetIndex = target.beforeTransfer(); 925 try { 926 long n = transferFromFileDescriptor(src.fd, position, count); 927 if (n == IOStatus.UNSUPPORTED) { 928 transferFromDirectNotSupported = true; 929 return IOStatus.UNSUPPORTED; 930 } 931 completed = (n >= 0); 932 return IOStatus.normalize(n); 933 } finally { 934 target.afterTransfer(completed, targetIndex); 935 } 936 } finally { 937 src.afterTransfer(completed, srcIndex); 938 } 939 } finally { 940 endBlocking(completed); 941 } 942 } 943 944 /** 945 * Transfers bytes into this channel's file from the given channel's file. This 946 * implementation memory maps the given channel's file. 947 */ 948 private long transferFromFileChannel(FileChannelImpl src, long position, long count) 949 throws IOException 950 { 951 if (count < MAPPED_TRANSFER_THRESHOLD) 952 return IOStatus.UNSUPPORTED_CASE; 953 954 synchronized (src.positionLock) { 955 long pos = src.position(); 956 long max = Math.min(count, src.size() - pos); 957 958 if (src == this 959 && (position() - max + 1 <= pos) 960 && (pos - max + 1 <= position()) 961 && !nd.canTransferToFromOverlappedMap()) { 962 return IOStatus.UNSUPPORTED_CASE; 963 } 964 965 long remaining = max; 966 long p = pos; 967 while (remaining > 0L) { 968 long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); 969 MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size); 970 try { 971 long n = write(bb, position); 972 assert n > 0; 973 p += n; 974 position += n; 975 remaining -= n; 976 } catch (IOException ioe) { 977 // Only throw exception if no bytes have been written 978 if (remaining == max) 979 throw ioe; 980 break; 981 } finally { 982 unmap(bb); 983 } 984 } 985 long nwritten = max - remaining; 986 src.position(pos + nwritten); 987 return nwritten; 988 } 989 } 990 991 private static final int TRANSFER_SIZE = 8192; 992 993 /** 994 * Transfers bytes into this channel's file from the given channel. 995 */ 996 private long transferFromArbitraryChannel(ReadableByteChannel src, 997 long position, long count) 998 throws IOException 999 { 1000 int c = (int) Math.min(count, TRANSFER_SIZE); 1001 ByteBuffer bb = ByteBuffer.allocate(c); 1002 long tw = 0; // Total bytes written 1003 long pos = position; 1004 try { 1005 while (tw < count) { 1006 bb.limit((int) Math.min((count - tw), TRANSFER_SIZE)); 1007 // read may block, closing this channel will not wake it up 1008 int nr = src.read(bb); 1009 if (nr <= 0) 1010 break; 1011 bb.flip(); 1012 int nw = write(bb, pos); 1013 tw += nw; 1014 if (nw != nr) 1015 break; 1016 pos += nw; 1017 bb.clear(); 1018 } 1019 return tw; 1020 } catch (IOException x) { 1021 if (tw > 0) 1022 return tw; 1023 throw x; 1024 } 1025 } 1026 1027 @Override 1028 public long transferFrom(ReadableByteChannel src, long position, long count) 1029 throws IOException 1030 { 1031 ensureOpen(); 1032 if (!src.isOpen()) 1033 throw new ClosedChannelException(); 1034 if (src instanceof FileChannelImpl fci && !fci.readable) 1035 throw new NonReadableChannelException(); 1036 if (!writable) 1037 throw new NonWritableChannelException(); 1038 if ((position < 0) || (count < 0)) 1039 throw new IllegalArgumentException(); 1040 1041 try { 1042 // System calls supporting fast transfers might not work on files 1043 // which advertise zero size such as those in Linux /proc 1044 if (src instanceof FileChannelImpl fci && fci.size() > 0) { 1045 long n; 1046 if ((n = transferFromDirect(fci, position, count)) >= 0) 1047 return n; 1048 if ((n = transferFromFileChannel(fci, position, count)) >= 0) 1049 return n; 1050 } 1051 1052 // fallback to read/write loop 1053 return transferFromArbitraryChannel(src, position, count); 1054 } catch (ClosedChannelException e) { 1055 // throw AsynchronousCloseException or ClosedByInterruptException 1056 throw transferFailed(e, src); 1057 } 1058 } 1059 1060 @Override 1061 public int read(ByteBuffer dst, long position) throws IOException { 1062 if (dst == null) 1063 throw new NullPointerException(); 1064 if (position < 0) 1065 throw new IllegalArgumentException("Negative position"); 1066 ensureOpen(); 1067 if (!readable) 1068 throw new NonReadableChannelException(); 1069 if (direct) 1070 Util.checkChannelPositionAligned(position, alignment); 1071 if (nd.needsPositionLock()) { 1072 synchronized (positionLock) { 1073 return readInternal(dst, position); 1074 } 1075 } else { 1076 return readInternal(dst, position); 1077 } 1078 } 1079 1080 private int readInternal(ByteBuffer dst, long position) throws IOException { 1081 assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); 1082 int n = 0; 1083 int ti = -1; 1084 1085 try { 1086 beginBlocking(); 1087 ti = threads.add(); 1088 if (!isOpen()) 1089 return -1; 1090 do { 1091 long comp = Blocker.begin(); 1092 try { 1093 n = IOUtil.read(fd, dst, position, direct, alignment, nd); 1094 } finally { 1095 Blocker.end(comp); 1096 } 1097 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 1098 return IOStatus.normalize(n); 1099 } finally { 1100 threads.remove(ti); 1101 endBlocking(n > 0); 1102 assert IOStatus.check(n); 1103 } 1104 } 1105 1106 @Override 1107 public int write(ByteBuffer src, long position) throws IOException { 1108 if (src == null) 1109 throw new NullPointerException(); 1110 if (position < 0) 1111 throw new IllegalArgumentException("Negative position"); 1112 ensureOpen(); 1113 if (!writable) 1114 throw new NonWritableChannelException(); 1115 if (direct) 1116 Util.checkChannelPositionAligned(position, alignment); 1117 if (nd.needsPositionLock()) { 1118 synchronized (positionLock) { 1119 return writeInternal(src, position); 1120 } 1121 } else { 1122 return writeInternal(src, position); 1123 } 1124 } 1125 1126 private int writeInternal(ByteBuffer src, long position) throws IOException { 1127 assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); 1128 int n = 0; 1129 int ti = -1; 1130 try { 1131 beginBlocking(); 1132 ti = threads.add(); 1133 if (!isOpen()) 1134 return -1; 1135 do { 1136 long comp = Blocker.begin(); 1137 try { 1138 n = IOUtil.write(fd, src, position, direct, alignment, nd); 1139 } finally { 1140 Blocker.end(comp); 1141 } 1142 } while ((n == IOStatus.INTERRUPTED) && isOpen()); 1143 return IOStatus.normalize(n); 1144 } finally { 1145 threads.remove(ti); 1146 endBlocking(n > 0); 1147 assert IOStatus.check(n); 1148 } 1149 } 1150 1151 1152 // -- Memory-mapped buffers -- 1153 1154 private abstract static sealed class Unmapper 1155 implements Runnable, UnmapperProxy 1156 { 1157 private final long address; 1158 protected final long size; 1159 protected final long cap; 1160 private final FileDescriptor fd; 1161 private final int pagePosition; 1162 1163 private Unmapper(long address, long size, long cap, 1164 FileDescriptor fd, int pagePosition) 1165 { 1166 assert (address != 0); 1167 this.address = address; 1168 this.size = size; 1169 this.cap = cap; 1170 this.fd = fd; 1171 this.pagePosition = pagePosition; 1172 } 1173 1174 @Override 1175 public long address() { 1176 return address + pagePosition; 1177 } 1178 1179 @Override 1180 public FileDescriptor fileDescriptor() { 1181 return fd; 1182 } 1183 1184 @Override 1185 public void run() { 1186 unmap(); 1187 } 1188 1189 public long capacity() { 1190 return cap; 1191 } 1192 1193 public void unmap() { 1194 nd.unmap(address, size); 1195 1196 // if this mapping has a valid file descriptor then we close it 1197 if (fd.valid()) { 1198 try { 1199 nd.close(fd); 1200 } catch (IOException ignore) { 1201 // nothing we can do 1202 } 1203 } 1204 1205 decrementStats(); 1206 } 1207 protected abstract void incrementStats(); 1208 protected abstract void decrementStats(); 1209 } 1210 1211 private static final class DefaultUnmapper extends Unmapper { 1212 1213 // keep track of non-sync mapped buffer usage 1214 static volatile int count; 1215 static volatile long totalSize; 1216 static volatile long totalCapacity; 1217 1218 public DefaultUnmapper(long address, long size, long cap, 1219 FileDescriptor fd, int pagePosition) { 1220 super(address, size, cap, fd, pagePosition); 1221 incrementStats(); 1222 } 1223 1224 protected void incrementStats() { 1225 synchronized (DefaultUnmapper.class) { 1226 count++; 1227 totalSize += size; 1228 totalCapacity += cap; 1229 } 1230 } 1231 protected void decrementStats() { 1232 synchronized (DefaultUnmapper.class) { 1233 count--; 1234 totalSize -= size; 1235 totalCapacity -= cap; 1236 } 1237 } 1238 1239 public boolean isSync() { 1240 return false; 1241 } 1242 } 1243 1244 private static final class SyncUnmapper extends Unmapper { 1245 1246 // keep track of mapped buffer usage 1247 static volatile int count; 1248 static volatile long totalSize; 1249 static volatile long totalCapacity; 1250 1251 public SyncUnmapper(long address, long size, long cap, 1252 FileDescriptor fd, int pagePosition) { 1253 super(address, size, cap, fd, pagePosition); 1254 incrementStats(); 1255 } 1256 1257 protected void incrementStats() { 1258 synchronized (SyncUnmapper.class) { 1259 count++; 1260 totalSize += size; 1261 totalCapacity += cap; 1262 } 1263 } 1264 protected void decrementStats() { 1265 synchronized (SyncUnmapper.class) { 1266 count--; 1267 totalSize -= size; 1268 totalCapacity -= cap; 1269 } 1270 } 1271 1272 public boolean isSync() { 1273 return true; 1274 } 1275 } 1276 1277 private static void unmap(MappedByteBuffer bb) { 1278 Cleaner cl = ((DirectBuffer)bb).cleaner(); 1279 if (cl != null) 1280 cl.clean(); 1281 } 1282 1283 private static final int MAP_INVALID = -1; 1284 private static final int MAP_RO = 0; 1285 private static final int MAP_RW = 1; 1286 private static final int MAP_PV = 2; 1287 1288 @Override 1289 public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException { 1290 if (size > Integer.MAX_VALUE) 1291 throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE"); 1292 boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null")); 1293 int prot = toProt(mode); 1294 Unmapper unmapper = mapInternal(mode, position, size, prot, isSync); 1295 if (unmapper == null) { 1296 // a valid file descriptor is not required 1297 FileDescriptor dummy = new FileDescriptor(); 1298 if ((!writable) || (prot == MAP_RO)) 1299 return Util.newMappedByteBufferR(0, 0, dummy, null, isSync); 1300 else 1301 return Util.newMappedByteBuffer(0, 0, dummy, null, isSync); 1302 } else if ((!writable) || (prot == MAP_RO)) { 1303 return Util.newMappedByteBufferR((int)unmapper.capacity(), 1304 unmapper.address(), 1305 unmapper.fileDescriptor(), 1306 unmapper, unmapper.isSync()); 1307 } else { 1308 return Util.newMappedByteBuffer((int)unmapper.capacity(), 1309 unmapper.address(), 1310 unmapper.fileDescriptor(), 1311 unmapper, unmapper.isSync()); 1312 } 1313 } 1314 1315 @Override 1316 public MemorySegment map(MapMode mode, long offset, long size, Arena arena) 1317 throws IOException 1318 { 1319 Objects.requireNonNull(mode,"Mode is null"); 1320 Objects.requireNonNull(arena, "Arena is null"); 1321 MemorySessionImpl sessionImpl = MemorySessionImpl.toMemorySession(arena); 1322 sessionImpl.checkValidState(); 1323 if (offset < 0) 1324 throw new IllegalArgumentException("Requested bytes offset must be >= 0."); 1325 if (size < 0) 1326 throw new IllegalArgumentException("Requested bytes size must be >= 0."); 1327 1328 boolean isSync = isSync(mode); 1329 int prot = toProt(mode); 1330 Unmapper unmapper = mapInternal(mode, offset, size, prot, isSync); 1331 boolean readOnly = false; 1332 if (mode == MapMode.READ_ONLY) { 1333 readOnly = true; 1334 } 1335 return SegmentFactories.mapSegment(size, unmapper, readOnly, sessionImpl); 1336 } 1337 1338 private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync) 1339 throws IOException 1340 { 1341 ensureOpen(); 1342 if (mode == null) 1343 throw new NullPointerException("Mode is null"); 1344 if (position < 0L) 1345 throw new IllegalArgumentException("Negative position"); 1346 if (size < 0L) 1347 throw new IllegalArgumentException("Negative size"); 1348 if (position + size < 0) 1349 throw new IllegalArgumentException("Position + size overflow"); 1350 1351 checkMode(mode, prot, isSync); 1352 long addr = -1; 1353 int ti = -1; 1354 try { 1355 beginBlocking(); 1356 ti = threads.add(); 1357 if (!isOpen()) 1358 return null; 1359 1360 long mapSize; 1361 int pagePosition; 1362 synchronized (positionLock) { 1363 long filesize; 1364 do { 1365 long comp = Blocker.begin(); 1366 try { 1367 filesize = nd.size(fd); 1368 } finally { 1369 Blocker.end(comp); 1370 } 1371 } while ((filesize == IOStatus.INTERRUPTED) && isOpen()); 1372 if (!isOpen()) 1373 return null; 1374 1375 if (filesize < position + size) { // Extend file size 1376 if (!writable) { 1377 throw new IOException("Channel not open for writing " + 1378 "- cannot extend file to required size"); 1379 } 1380 int rv; 1381 do { 1382 long comp = Blocker.begin(); 1383 try { 1384 rv = nd.truncate(fd, position + size); 1385 } finally { 1386 Blocker.end(comp); 1387 } 1388 } while ((rv == IOStatus.INTERRUPTED) && isOpen()); 1389 if (!isOpen()) 1390 return null; 1391 } 1392 1393 if (size == 0) { 1394 return null; 1395 } 1396 1397 pagePosition = (int)(position % nd.allocationGranularity()); 1398 long mapPosition = position - pagePosition; 1399 mapSize = size + pagePosition; 1400 try { 1401 // If map did not throw an exception, the address is valid 1402 addr = nd.map(fd, prot, mapPosition, mapSize, isSync); 1403 } catch (OutOfMemoryError x) { 1404 // An OutOfMemoryError may indicate that we've exhausted 1405 // memory so force gc and re-attempt map 1406 System.gc(); 1407 try { 1408 Thread.sleep(100); 1409 } catch (InterruptedException y) { 1410 Thread.currentThread().interrupt(); 1411 } 1412 try { 1413 addr = nd.map(fd, prot, mapPosition, mapSize, isSync); 1414 } catch (OutOfMemoryError y) { 1415 // After a second OOME, fail 1416 throw new IOException("Map failed", y); 1417 } 1418 } 1419 } // synchronized 1420 1421 // On Windows, and potentially other platforms, we need an open 1422 // file descriptor for some mapping operations. 1423 FileDescriptor mfd; 1424 try { 1425 mfd = nd.duplicateForMapping(fd); 1426 } catch (IOException ioe) { 1427 nd.unmap(addr, mapSize); 1428 throw ioe; 1429 } 1430 1431 assert (IOStatus.checkAll(addr)); 1432 assert (addr % nd.allocationGranularity() == 0); 1433 Unmapper um = (isSync 1434 ? new SyncUnmapper(addr, mapSize, size, mfd, pagePosition) 1435 : new DefaultUnmapper(addr, mapSize, size, mfd, pagePosition)); 1436 return um; 1437 } finally { 1438 threads.remove(ti); 1439 endBlocking(IOStatus.checkAll(addr)); 1440 } 1441 } 1442 1443 private boolean isSync(MapMode mode) { 1444 // Do not want to initialize ExtendedMapMode until 1445 // after the module system has been initialized 1446 return !VM.isModuleSystemInited() ? false : 1447 (mode == ExtendedMapMode.READ_ONLY_SYNC || 1448 mode == ExtendedMapMode.READ_WRITE_SYNC); 1449 } 1450 1451 private int toProt(MapMode mode) { 1452 int prot; 1453 if (mode == MapMode.READ_ONLY) { 1454 prot = MAP_RO; 1455 } else if (mode == MapMode.READ_WRITE) { 1456 prot = MAP_RW; 1457 } else if (mode == MapMode.PRIVATE) { 1458 prot = MAP_PV; 1459 } else if (mode == ExtendedMapMode.READ_ONLY_SYNC) { 1460 prot = MAP_RO; 1461 } else if (mode == ExtendedMapMode.READ_WRITE_SYNC) { 1462 prot = MAP_RW; 1463 } else { 1464 prot = MAP_INVALID; 1465 } 1466 return prot; 1467 } 1468 1469 private void checkMode(MapMode mode, int prot, boolean isSync) { 1470 if (prot == MAP_INVALID) { 1471 throw new UnsupportedOperationException(); 1472 } 1473 if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable) 1474 throw new NonWritableChannelException(); 1475 if (!readable) 1476 throw new NonReadableChannelException(); 1477 // reject SYNC request if writeback is not enabled for this platform 1478 if (isSync && !Unsafe.isWritebackEnabled()) { 1479 throw new UnsupportedOperationException(); 1480 } 1481 } 1482 1483 /** 1484 * Invoked by sun.management.ManagementFactoryHelper to create the management 1485 * interface for mapped buffers. 1486 */ 1487 public static BufferPool getMappedBufferPool() { 1488 return new BufferPool() { 1489 @Override 1490 public String getName() { 1491 return "mapped"; 1492 } 1493 @Override 1494 public long getCount() { 1495 return DefaultUnmapper.count; 1496 } 1497 @Override 1498 public long getTotalCapacity() { 1499 return DefaultUnmapper.totalCapacity; 1500 } 1501 @Override 1502 public long getMemoryUsed() { 1503 return DefaultUnmapper.totalSize; 1504 } 1505 }; 1506 } 1507 1508 /** 1509 * Invoked by sun.management.ManagementFactoryHelper to create the management 1510 * interface for sync mapped buffers. 1511 */ 1512 public static BufferPool getSyncMappedBufferPool() { 1513 return new BufferPool() { 1514 @Override 1515 public String getName() { 1516 return "mapped - 'non-volatile memory'"; 1517 } 1518 @Override 1519 public long getCount() { 1520 return SyncUnmapper.count; 1521 } 1522 @Override 1523 public long getTotalCapacity() { 1524 return SyncUnmapper.totalCapacity; 1525 } 1526 @Override 1527 public long getMemoryUsed() { 1528 return SyncUnmapper.totalSize; 1529 } 1530 }; 1531 } 1532 1533 // -- Locks -- 1534 1535 // keeps track of locks on this file 1536 private volatile FileLockTable fileLockTable; 1537 1538 private FileLockTable fileLockTable() throws IOException { 1539 if (fileLockTable == null) { 1540 synchronized (this) { 1541 if (fileLockTable == null) { 1542 int ti = threads.add(); 1543 try { 1544 ensureOpen(); 1545 fileLockTable = new FileLockTable(this, fd); 1546 } finally { 1547 threads.remove(ti); 1548 } 1549 } 1550 } 1551 } 1552 return fileLockTable; 1553 } 1554 1555 @Override 1556 public FileLock lock(long position, long size, boolean shared) 1557 throws IOException 1558 { 1559 ensureOpen(); 1560 if (shared && !readable) 1561 throw new NonReadableChannelException(); 1562 if (!shared && !writable) 1563 throw new NonWritableChannelException(); 1564 if (size == 0) 1565 size = Long.MAX_VALUE - Math.max(0, position); 1566 FileLockImpl fli = new FileLockImpl(this, position, size, shared); 1567 FileLockTable flt = fileLockTable(); 1568 flt.add(fli); 1569 boolean completed = false; 1570 int ti = -1; 1571 try { 1572 beginBlocking(); 1573 ti = threads.add(); 1574 if (!isOpen()) 1575 return null; 1576 int n; 1577 do { 1578 long comp = Blocker.begin(); 1579 try { 1580 n = nd.lock(fd, true, position, size, shared); 1581 } finally { 1582 Blocker.end(comp); 1583 } 1584 } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); 1585 if (isOpen()) { 1586 if (n == FileDispatcher.RET_EX_LOCK) { 1587 assert shared; 1588 FileLockImpl fli2 = new FileLockImpl(this, position, size, 1589 false); 1590 flt.replace(fli, fli2); 1591 fli = fli2; 1592 } 1593 completed = true; 1594 } 1595 } finally { 1596 if (!completed) 1597 flt.remove(fli); 1598 threads.remove(ti); 1599 try { 1600 endBlocking(completed); 1601 } catch (ClosedByInterruptException e) { 1602 throw new FileLockInterruptionException(); 1603 } 1604 } 1605 return fli; 1606 } 1607 1608 @Override 1609 public FileLock tryLock(long position, long size, boolean shared) 1610 throws IOException 1611 { 1612 ensureOpen(); 1613 if (shared && !readable) 1614 throw new NonReadableChannelException(); 1615 if (!shared && !writable) 1616 throw new NonWritableChannelException(); 1617 if (size == 0) 1618 size = Long.MAX_VALUE - Math.max(0, position); 1619 FileLockImpl fli = new FileLockImpl(this, position, size, shared); 1620 FileLockTable flt = fileLockTable(); 1621 flt.add(fli); 1622 int result; 1623 1624 int ti = threads.add(); 1625 try { 1626 try { 1627 ensureOpen(); 1628 result = nd.lock(fd, false, position, size, shared); 1629 } catch (IOException e) { 1630 flt.remove(fli); 1631 throw e; 1632 } 1633 if (result == FileDispatcher.NO_LOCK) { 1634 flt.remove(fli); 1635 return null; 1636 } 1637 if (result == FileDispatcher.RET_EX_LOCK) { 1638 assert shared; 1639 FileLockImpl fli2 = new FileLockImpl(this, position, size, 1640 false); 1641 flt.replace(fli, fli2); 1642 return fli2; 1643 } 1644 return fli; 1645 } finally { 1646 threads.remove(ti); 1647 } 1648 } 1649 1650 void release(FileLockImpl fli) throws IOException { 1651 int ti = threads.add(); 1652 try { 1653 ensureOpen(); 1654 nd.release(fd, fli.position(), fli.size()); 1655 } finally { 1656 threads.remove(ti); 1657 } 1658 assert fileLockTable != null; 1659 fileLockTable.remove(fli); 1660 } 1661 }