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 }