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