1 /*
   2  * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.nio.ch;
  27 
  28 import java.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.InterruptedIOException;
  32 import java.io.OutputStream;
  33 import java.io.UncheckedIOException;
  34 import java.lang.ref.Cleaner.Cleanable;
  35 import java.net.InetAddress;
  36 import java.net.InetSocketAddress;
  37 import java.net.ProtocolFamily;
  38 import java.net.SocketAddress;
  39 import java.net.SocketException;
  40 import java.net.SocketImpl;
  41 import java.net.SocketOption;
  42 import java.net.SocketTimeoutException;
  43 import java.net.StandardProtocolFamily;
  44 import java.net.StandardSocketOptions;
  45 import java.net.UnknownHostException;
  46 import java.nio.ByteBuffer;
  47 import java.util.Collections;
  48 import java.util.HashSet;
  49 import java.util.Objects;
  50 import java.util.Set;
  51 import java.util.concurrent.TimeUnit;
  52 import java.util.concurrent.locks.ReentrantLock;
  53 
  54 import jdk.internal.access.JavaIOFileDescriptorAccess;
  55 import jdk.internal.access.SharedSecrets;
  56 import jdk.internal.ref.CleanerFactory;
  57 import sun.net.ConnectionResetException;
  58 import sun.net.PlatformSocketImpl;
  59 import sun.net.ext.ExtendedSocketOptions;
  60 import jdk.internal.util.Exceptions;
  61 
  62 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  63 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  64 import static jdk.internal.util.Exceptions.filterNonSocketInfo;
  65 import static jdk.internal.util.Exceptions.formatMsg;
  66 
  67 /**
  68  * NIO based SocketImpl.
  69  *
  70  * The underlying socket used by this SocketImpl is initially configured blocking.
  71  * If a connect, accept or read is attempted with a timeout, or a virtual
  72  * thread invokes a blocking operation, then the socket is changed to non-blocking
  73  * When in non-blocking mode, operations that don't complete immediately will
  74  * poll the socket (or park when invoked on a virtual thread) and preserve
  75  * the semantics of blocking operations.
  76  */
  77 
  78 public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
  79     private static final NativeDispatcher nd = new SocketDispatcher();
  80 
  81     // true if this is a SocketImpl for a ServerSocket
  82     private final boolean server;
  83 
  84     // Lock held when reading (also used when accepting or connecting)
  85     private final ReentrantLock readLock = new ReentrantLock();
  86 
  87     // Lock held when writing
  88     private final ReentrantLock writeLock = new ReentrantLock();
  89 
  90     // The stateLock for read/changing state
  91     private final Object stateLock = new Object();
  92     private static final int ST_NEW = 0;
  93     private static final int ST_UNCONNECTED = 1;
  94     private static final int ST_CONNECTING = 2;
  95     private static final int ST_CONNECTED = 3;
  96     private static final int ST_CLOSING = 4;
  97     private static final int ST_CLOSED = 5;
  98     private volatile int state;  // need stateLock to change
  99 
 100     private Cleanable cleaner;
 101 
 102     // set to true when the socket is in non-blocking mode
 103     private volatile boolean nonBlocking;
 104 
 105     // used by connect/read/write/accept, protected by stateLock
 106     private long readerThread;
 107     private long writerThread;
 108 
 109     // used when SO_REUSEADDR is emulated, protected by stateLock
 110     private boolean isReuseAddress;
 111 
 112     // read or accept timeout in millis
 113     private volatile int timeout;
 114 
 115     // flags to indicate if the connection is shutdown for input and output
 116     private volatile boolean isInputClosed;
 117     private volatile boolean isOutputClosed;
 118 
 119     // used by read to emulate legacy behavior, protected by readLock
 120     private boolean readEOF;
 121     private boolean connectionReset;
 122 
 123     /**
 124      * Creates an instance of this SocketImpl.
 125      * @param server true if this is a SocketImpl for a ServerSocket
 126      */
 127     public NioSocketImpl(boolean server) {
 128         this.server = server;
 129     }
 130 
 131     /**
 132      * Returns true if the socket is open.
 133      */
 134     private boolean isOpen() {
 135         return state < ST_CLOSING;
 136     }
 137 
 138     /**
 139      * Throws SocketException if the socket is not open.
 140      */
 141     private void ensureOpen() throws SocketException {
 142         int state = this.state;
 143         if (state == ST_NEW)
 144             throw new SocketException("Socket not created");
 145         if (state >= ST_CLOSING)
 146             throw new SocketException("Socket closed");
 147     }
 148 
 149     /**
 150      * Throws SocketException if the socket is not open and connected.
 151      */
 152     private void ensureOpenAndConnected() throws SocketException {
 153         int state = this.state;
 154         if (state < ST_CONNECTED)
 155             throw new SocketException("Not connected");
 156         if (state > ST_CONNECTED)
 157             throw new SocketException("Socket closed");
 158     }
 159 
 160     /**
 161      * Disables the current thread for scheduling purposes until the
 162      * socket is ready for I/O or is asynchronously closed, for up to the
 163      * specified waiting time.
 164      * @throws IOException if an I/O error occurs
 165      */
 166     private void park(FileDescriptor fd, int event, long nanos) throws IOException {
 167         Thread t = Thread.currentThread();
 168         if (t.isVirtual()) {
 169             Poller.poll(fdVal(fd), event, nanos, this::isOpen);
 170             if (t.isInterrupted()) {
 171                 throw new InterruptedIOException();
 172             }
 173         } else {
 174             long millis;
 175             if (nanos == 0) {
 176                 millis = -1;
 177             } else {
 178                 millis = NANOSECONDS.toMillis(nanos);
 179                 if (nanos > MILLISECONDS.toNanos(millis)) {
 180                     // Round up any excess nanos to the nearest millisecond to
 181                     // avoid parking for less than requested.
 182                     millis++;
 183                 }
 184             }
 185             Net.poll(fd, event, millis);
 186         }
 187     }
 188 
 189     /**
 190      * Disables the current thread for scheduling purposes until the socket is
 191      * ready for I/O or is asynchronously closed.
 192      * @throws IOException if an I/O error occurs
 193      */
 194     private void park(FileDescriptor fd, int event) throws IOException {
 195         park(fd, event, 0);
 196     }
 197 
 198     /**
 199      * Ensures that the socket is configured non-blocking invoked on a virtual
 200      * thread or the operation has a timeout
 201      * @throws IOException if there is an I/O error changing the blocking mode
 202      */
 203     private void configureNonBlockingIfNeeded(FileDescriptor fd, boolean timed)
 204         throws IOException
 205     {
 206         if (!nonBlocking
 207             && (timed || Thread.currentThread().isVirtual())) {
 208             assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
 209             IOUtil.configureBlocking(fd, false);
 210             nonBlocking = true;
 211         }
 212     }
 213 
 214     /**
 215      * Marks the beginning of a read operation that might block.
 216      * @throws SocketException if the socket is closed or not connected
 217      */
 218     private FileDescriptor beginRead() throws SocketException {
 219         synchronized (stateLock) {
 220             ensureOpenAndConnected();
 221             readerThread = NativeThread.current();
 222             return fd;
 223         }
 224     }
 225 
 226     /**
 227      * Marks the end of a read operation that may have blocked.
 228      * @throws SocketException is the socket is closed
 229      */
 230     private void endRead(boolean completed) throws SocketException {
 231         synchronized (stateLock) {
 232             readerThread = 0;
 233             int state = this.state;
 234             if (state == ST_CLOSING)
 235                 tryFinishClose();
 236             if (!completed && state >= ST_CLOSING)
 237                 throw new SocketException("Socket closed");
 238         }
 239     }
 240 
 241     /**
 242      * Attempts to read bytes from the socket into the given byte array.
 243      */
 244     private int tryRead(FileDescriptor fd, byte[] b, int off, int len)
 245         throws IOException
 246     {
 247         ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
 248         assert dst.position() == 0;
 249         try {
 250             int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
 251             if (n > 0) {
 252                 dst.get(b, off, n);
 253             }
 254             return n;
 255         } finally {
 256             Util.offerFirstTemporaryDirectBuffer(dst);
 257         }
 258     }
 259 
 260     /**
 261      * Reads bytes from the socket into the given byte array with a timeout.
 262      * @throws SocketTimeoutException if the read timeout elapses
 263      */
 264     private int timedRead(FileDescriptor fd, byte[] b, int off, int len, long nanos)
 265         throws IOException
 266     {
 267         long startNanos = System.nanoTime();
 268         int n = tryRead(fd, b, off, len);
 269         while (n == IOStatus.UNAVAILABLE && isOpen()) {
 270             long remainingNanos = nanos - (System.nanoTime() - startNanos);
 271             if (remainingNanos <= 0) {
 272                 throw new SocketTimeoutException("Read timed out");
 273             }
 274             park(fd, Net.POLLIN, remainingNanos);
 275             n = tryRead(fd, b, off, len);
 276         }
 277         return n;
 278     }
 279 
 280     /**
 281      * Reads bytes from the socket into the given byte array.
 282      * @return the number of bytes read or -1 at EOF
 283      * @throws SocketException if the socket is closed or a socket I/O error occurs
 284      * @throws SocketTimeoutException if the read timeout elapses
 285      */
 286     private int implRead(byte[] b, int off, int len, long remainingNanos) throws IOException {
 287         int n = 0;
 288         SocketException ex = null;
 289         FileDescriptor fd = beginRead();
 290         try {
 291             if (connectionReset)
 292                 throw new SocketException("Connection reset");
 293             if (isInputClosed)
 294                 return -1;
 295             configureNonBlockingIfNeeded(fd, remainingNanos > 0);
 296             if (remainingNanos > 0) {
 297                 // read with timeout
 298                 n = timedRead(fd, b, off, len, remainingNanos);
 299             } else {
 300                 // read, no timeout
 301                 n = tryRead(fd, b, off, len);
 302                 while (IOStatus.okayToRetry(n) && isOpen()) {
 303                     park(fd, Net.POLLIN);
 304                     n = tryRead(fd, b, off, len);
 305                 }
 306             }
 307         } catch (InterruptedIOException e) {
 308             throw e;
 309         } catch (ConnectionResetException e) {
 310             connectionReset = true;
 311             throw new SocketException("Connection reset");
 312         } catch (IOException ioe) {
 313             // translate to SocketException to maintain compatibility
 314             ex = asSocketException(ioe);
 315         } finally {
 316             endRead(n > 0);
 317         }
 318         if (n <= 0 && isInputClosed) {
 319             return -1;
 320         }
 321         if (ex != null) {
 322             throw ex;
 323         }
 324         return n;
 325     }
 326 
 327     /**
 328      * Reads bytes from the socket into the given byte array.
 329      * @return the number of bytes read or -1 at EOF
 330      * @throws IndexOutOfBoundsException if the bound checks fail
 331      * @throws SocketException if the socket is closed or a socket I/O error occurs
 332      * @throws SocketTimeoutException if the read timeout elapses
 333      */
 334     private int read(byte[] b, int off, int len) throws IOException {
 335         Objects.checkFromIndexSize(off, len, b.length);
 336         if (len == 0) {
 337             return 0;
 338         } else {
 339             long remainingNanos = 0;
 340             int timeout = this.timeout;
 341             if (timeout > 0) {
 342                 remainingNanos = tryLock(readLock, timeout, MILLISECONDS);
 343                 if (remainingNanos <= 0) {
 344                     assert !readLock.isHeldByCurrentThread();
 345                     throw new SocketTimeoutException("Read timed out");
 346                 }
 347             } else {
 348                 readLock.lock();
 349             }
 350             try {
 351                 // emulate legacy behavior to return -1, even if socket is closed
 352                 if (readEOF)
 353                     return -1;
 354                 // read up to Streams.MAX_BUFFER_SIZE bytes
 355                 int size = Math.min(len, Streams.MAX_BUFFER_SIZE);
 356                 int n = implRead(b, off, size, remainingNanos);
 357                 if (n == -1)
 358                     readEOF = true;
 359                 return n;
 360             } finally {
 361                 readLock.unlock();
 362             }
 363         }
 364     }
 365 
 366     /**
 367      * Marks the beginning of a write operation that might block.
 368      * @throws SocketException if the socket is closed or not connected
 369      */
 370     private FileDescriptor beginWrite() throws SocketException {
 371         synchronized (stateLock) {
 372             ensureOpenAndConnected();
 373             writerThread = NativeThread.current();
 374             return fd;
 375         }
 376     }
 377 
 378     /**
 379      * Marks the end of a write operation that may have blocked.
 380      * @throws SocketException is the socket is closed
 381      */
 382     private void endWrite(boolean completed) throws SocketException {
 383         synchronized (stateLock) {
 384             writerThread = 0;
 385             int state = this.state;
 386             if (state == ST_CLOSING)
 387                 tryFinishClose();
 388             if (!completed && state >= ST_CLOSING)
 389                 throw new SocketException("Socket closed");
 390         }
 391     }
 392 
 393     /**
 394      * Attempts to write a sequence of bytes to the socket from the given
 395      * byte array.
 396      */
 397     private int tryWrite(FileDescriptor fd, byte[] b, int off, int len)
 398         throws IOException
 399     {
 400         ByteBuffer src = Util.getTemporaryDirectBuffer(len);
 401         assert src.position() == 0;
 402         try {
 403             src.put(b, off, len);
 404             return nd.write(fd, ((DirectBuffer)src).address(), len);
 405         } finally {
 406             Util.offerFirstTemporaryDirectBuffer(src);
 407         }
 408     }
 409 
 410     /**
 411      * Writes a sequence of bytes to the socket from the given byte array.
 412      * @return the number of bytes written
 413      * @throws SocketException if the socket is closed or a socket I/O error occurs
 414      */
 415     private int implWrite(byte[] b, int off, int len) throws IOException {
 416         int n = 0;
 417         SocketException ex = null;
 418         FileDescriptor fd = beginWrite();
 419         try {
 420             configureNonBlockingIfNeeded(fd, false);
 421             n = tryWrite(fd, b, off, len);
 422             while (IOStatus.okayToRetry(n) && isOpen()) {
 423                 park(fd, Net.POLLOUT);
 424                 n = tryWrite(fd, b, off, len);
 425             }
 426         } catch (InterruptedIOException e) {
 427             throw e;
 428         } catch (IOException ioe) {
 429             // translate to SocketException to maintain compatibility
 430             ex = asSocketException(ioe);
 431         } finally {
 432             endWrite(n > 0);
 433         }
 434         if (ex != null) {
 435             throw ex;
 436         }
 437         return n;
 438     }
 439 
 440     /**
 441      * Writes a sequence of bytes to the socket from the given byte array.
 442      * @throws SocketException if the socket is closed or a socket I/O error occurs
 443      */
 444     private void write(byte[] b, int off, int len) throws IOException {
 445         Objects.checkFromIndexSize(off, len, b.length);
 446         if (len > 0) {
 447             writeLock.lock();
 448             try {
 449                 int pos = off;
 450                 int end = off + len;
 451                 while (pos < end) {
 452                     // write up to Streams.MAX_BUFFER_SIZE bytes
 453                     int size = Math.min((end - pos), Streams.MAX_BUFFER_SIZE);
 454                     int n = implWrite(b, pos, size);
 455                     pos += n;
 456                 }
 457             } finally {
 458                 writeLock.unlock();
 459             }
 460         }
 461     }
 462 
 463     /**
 464      * Creates the socket.
 465      * @param stream {@code true} for a streams socket
 466      */
 467     @Override
 468     protected void create(boolean stream) throws IOException {
 469         if (!stream) {
 470             throw new IOException("Datagram socket creation not supported");
 471         }
 472         synchronized (stateLock) {
 473             if (state != ST_NEW)
 474                 throw new IOException("Already created");
 475             FileDescriptor fd;
 476             if (server) {
 477                 fd = Net.serverSocket();
 478             } else {
 479                 fd = Net.socket();
 480             }
 481             Runnable closer = closerFor(fd);
 482             this.fd = fd;
 483             this.cleaner = CleanerFactory.cleaner().register(this, closer);
 484             this.state = ST_UNCONNECTED;
 485         }
 486     }
 487 
 488     /**
 489      * Marks the beginning of a connect operation that might block.
 490      * @throws SocketException if the socket is closed or already connected
 491      */
 492     private FileDescriptor beginConnect(InetAddress address, int port)
 493         throws IOException
 494     {
 495         synchronized (stateLock) {
 496             int state = this.state;
 497             if (state != ST_UNCONNECTED) {
 498                 if (state == ST_NEW)
 499                     throw new SocketException("Not created");
 500                 if (state == ST_CONNECTING)
 501                     throw new SocketException("Connection in progress");
 502                 if (state == ST_CONNECTED)
 503                     throw new SocketException("Already connected");
 504                 if (state >= ST_CLOSING)
 505                     throw new SocketException("Socket closed");
 506                 assert false;
 507             }
 508             this.state = ST_CONNECTING;
 509 
 510             // save the remote address/port
 511             this.address = address;
 512             this.port = port;
 513 
 514             readerThread = NativeThread.current();
 515             return fd;
 516         }
 517     }
 518 
 519     /**
 520      * Marks the end of a connect operation that may have blocked.
 521      * @throws SocketException is the socket is closed
 522      */
 523     private void endConnect(FileDescriptor fd, boolean completed) throws IOException {
 524         synchronized (stateLock) {
 525             readerThread = 0;
 526             int state = this.state;
 527             if (state == ST_CLOSING)
 528                 tryFinishClose();
 529             if (completed && state == ST_CONNECTING) {
 530                 this.state = ST_CONNECTED;
 531                 localport = Net.localAddress(fd).getPort();
 532             } else if (!completed && state >= ST_CLOSING) {
 533                 throw new SocketException("Socket closed");
 534             }
 535         }
 536     }
 537 
 538     /**
 539      * Waits for a connection attempt to finish with a timeout
 540      * @throws SocketTimeoutException if the connect timeout elapses
 541      */
 542     private boolean timedFinishConnect(FileDescriptor fd, long nanos) throws IOException {
 543         long startNanos = System.nanoTime();
 544         boolean polled = Net.pollConnectNow(fd);
 545         while (!polled && isOpen()) {
 546             long remainingNanos = nanos - (System.nanoTime() - startNanos);
 547             if (remainingNanos <= 0) {
 548                 throw new SocketTimeoutException("Connect timed out");
 549             }
 550             park(fd, Net.POLLOUT, remainingNanos);
 551             polled = Net.pollConnectNow(fd);
 552         }
 553         return polled && isOpen();
 554     }
 555 
 556     /**
 557      * Attempts to establish a connection to the given socket address with a
 558      * timeout. Closes the socket if connection cannot be established.
 559      * @throws IOException if the address is not a resolved InetSocketAddress or
 560      *         the connection cannot be established
 561      */
 562     @Override
 563     protected void connect(SocketAddress remote, int millis) throws IOException {
 564         // SocketImpl connect only specifies IOException
 565         if (!(remote instanceof InetSocketAddress))
 566             throw new IOException("Unsupported address type");
 567         InetSocketAddress isa = (InetSocketAddress) remote;
 568         if (isa.isUnresolved()) {
 569             throw new UnknownHostException(
 570                 formatMsg(filterNonSocketInfo(isa.getHostName())));
 571         }
 572 
 573         InetAddress address = isa.getAddress();
 574         if (address.isAnyLocalAddress())
 575             address = InetAddress.getLocalHost();
 576         int port = isa.getPort();
 577 
 578         ReentrantLock connectLock = readLock;
 579         try {
 580             connectLock.lock();
 581             try {
 582                 boolean connected = false;
 583                 FileDescriptor fd = beginConnect(address, port);
 584                 try {
 585                     configureNonBlockingIfNeeded(fd, millis > 0);
 586                     int n = Net.connect(fd, address, port);
 587                     if (n > 0) {
 588                         // connection established
 589                         connected = true;
 590                     } else {
 591                         assert IOStatus.okayToRetry(n);
 592                         if (millis > 0) {
 593                             // finish connect with timeout
 594                             long nanos = MILLISECONDS.toNanos(millis);
 595                             connected = timedFinishConnect(fd, nanos);
 596                         } else {
 597                             // finish connect, no timeout
 598                             boolean polled = false;
 599                             while (!polled && isOpen()) {
 600                                 park(fd, Net.POLLOUT);
 601                                 polled = Net.pollConnectNow(fd);
 602                             }
 603                             connected = polled && isOpen();
 604                         }
 605                     }
 606                 } finally {
 607                     endConnect(fd, connected);
 608                 }
 609             } finally {
 610                 connectLock.unlock();
 611             }
 612         } catch (IOException ioe) {
 613             close();
 614             if (ioe instanceof SocketTimeoutException) {
 615                 throw ioe;
 616             } else if (ioe instanceof InterruptedIOException) {
 617                 assert Thread.currentThread().isVirtual();
 618                 throw new SocketException("Closed by interrupt");
 619             } else {
 620                 throw Exceptions.ioException(ioe, isa);
 621             }
 622         }
 623     }
 624 
 625     @Override
 626     protected void connect(String host, int port) throws IOException {
 627         connect(new InetSocketAddress(host, port), 0);
 628     }
 629 
 630     @Override
 631     protected void connect(InetAddress address, int port) throws IOException {
 632         connect(new InetSocketAddress(address, port), 0);
 633     }
 634 
 635     @Override
 636     protected void bind(InetAddress host, int port) throws IOException {
 637         synchronized (stateLock) {
 638             ensureOpen();
 639             if (localport != 0)
 640                 throw new SocketException("Already bound");
 641             Net.bind(fd, host, port);
 642             // set the address field to the given host address to
 643             // maintain long standing behavior. When binding to 0.0.0.0
 644             // then the actual local address will be ::0 when IPv6 is enabled.
 645             address = host;
 646             localport = Net.localAddress(fd).getPort();
 647         }
 648     }
 649 
 650     @Override
 651     protected void listen(int backlog) throws IOException {
 652         synchronized (stateLock) {
 653             ensureOpen();
 654             if (localport == 0)
 655                 throw new SocketException("Not bound");
 656             Net.listen(fd, backlog < 1 ? 50 : backlog);
 657         }
 658     }
 659 
 660     /**
 661      * Marks the beginning of an accept operation that might block.
 662      * @throws SocketException if the socket is closed
 663      */
 664     private FileDescriptor beginAccept() throws SocketException {
 665         synchronized (stateLock) {
 666             ensureOpen();
 667             if (localport == 0)
 668                 throw new SocketException("Not bound");
 669             readerThread = NativeThread.current();
 670             return fd;
 671         }
 672     }
 673 
 674     /**
 675      * Marks the end of an accept operation that may have blocked.
 676      * @throws SocketException is the socket is closed
 677      */
 678     private void endAccept(boolean completed) throws SocketException {
 679         synchronized (stateLock) {
 680             int state = this.state;
 681             readerThread = 0;
 682             if (state == ST_CLOSING)
 683                 tryFinishClose();
 684             if (!completed && state >= ST_CLOSING)
 685                 throw new SocketException("Socket closed");
 686         }
 687     }
 688 
 689     /**
 690      * Accepts a new connection with a timeout.
 691      * @throws SocketTimeoutException if the accept timeout elapses
 692      */
 693     private int timedAccept(FileDescriptor fd,
 694                             FileDescriptor newfd,
 695                             InetSocketAddress[] isaa,
 696                             long nanos)
 697         throws IOException
 698     {
 699         long startNanos = System.nanoTime();
 700         int n = Net.accept(fd, newfd, isaa);
 701         while (n == IOStatus.UNAVAILABLE && isOpen()) {
 702             long remainingNanos = nanos - (System.nanoTime() - startNanos);
 703             if (remainingNanos <= 0) {
 704                 throw new SocketTimeoutException("Accept timed out");
 705             }
 706             park(fd, Net.POLLIN, remainingNanos);
 707             n = Net.accept(fd, newfd, isaa);
 708         }
 709         return n;
 710     }
 711 
 712     /**
 713      * Accepts a new connection so that the given SocketImpl is connected to
 714      * the peer. The SocketImpl must be a newly created NioSocketImpl.
 715      */
 716     @Override
 717     protected void accept(SocketImpl si) throws IOException {
 718         NioSocketImpl nsi = (NioSocketImpl) si;
 719         if (nsi.state != ST_NEW)
 720             throw new SocketException("Not a newly created SocketImpl");
 721 
 722         FileDescriptor newfd = new FileDescriptor();
 723         InetSocketAddress[] isaa = new InetSocketAddress[1];
 724 
 725         // acquire the lock, adjusting the timeout for cases where several
 726         // threads are accepting connections and there is a timeout set
 727         ReentrantLock acceptLock = readLock;
 728         int timeout = this.timeout;
 729         long remainingNanos = 0;
 730         if (timeout > 0) {
 731             remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS);
 732             if (remainingNanos <= 0) {
 733                 assert !acceptLock.isHeldByCurrentThread();
 734                 throw new SocketTimeoutException("Accept timed out");
 735             }
 736         } else {
 737             acceptLock.lock();
 738         }
 739 
 740         // accept a connection
 741         try {
 742             int n = 0;
 743             FileDescriptor fd = beginAccept();
 744             try {
 745                 configureNonBlockingIfNeeded(fd, remainingNanos > 0);
 746                 if (remainingNanos > 0) {
 747                     // accept with timeout
 748                     n = timedAccept(fd, newfd, isaa, remainingNanos);
 749                 } else {
 750                     // accept, no timeout
 751                     n = Net.accept(fd, newfd, isaa);
 752                     while (IOStatus.okayToRetry(n) && isOpen()) {
 753                         park(fd, Net.POLLIN);
 754                         n = Net.accept(fd, newfd, isaa);
 755                     }
 756                 }
 757             } finally {
 758                 endAccept(n > 0);
 759                 assert IOStatus.check(n);
 760             }
 761         } finally {
 762             acceptLock.unlock();
 763         }
 764 
 765         // get local address and configure accepted socket to blocking mode
 766         InetSocketAddress localAddress;
 767         try {
 768             localAddress = Net.localAddress(newfd);
 769             IOUtil.configureBlocking(newfd, true);
 770         } catch (IOException ioe) {
 771             nd.close(newfd);
 772             throw ioe;
 773         }
 774 
 775         // set the fields
 776         Runnable closer = closerFor(newfd);
 777         synchronized (nsi.stateLock) {
 778             nsi.fd = newfd;
 779             nsi.cleaner = CleanerFactory.cleaner().register(nsi, closer);
 780             nsi.localport = localAddress.getPort();
 781             nsi.address = isaa[0].getAddress();
 782             nsi.port = isaa[0].getPort();
 783             nsi.state = ST_CONNECTED;
 784         }
 785     }
 786 
 787     @Override
 788     protected InputStream getInputStream() {
 789         return new InputStream() {
 790             @Override
 791             public int read() throws IOException {
 792                 byte[] a = new byte[1];
 793                 int n = read(a, 0, 1);
 794                 return (n > 0) ? (a[0] & 0xff) : -1;
 795             }
 796             @Override
 797             public int read(byte[] b, int off, int len) throws IOException {
 798                 return NioSocketImpl.this.read(b, off, len);
 799             }
 800             @Override
 801             public int available() throws IOException {
 802                 return NioSocketImpl.this.available();
 803             }
 804             @Override
 805             public void close() throws IOException {
 806                 NioSocketImpl.this.close();
 807             }
 808         };
 809     }
 810 
 811     @Override
 812     protected OutputStream getOutputStream() {
 813         return new OutputStream() {
 814             @Override
 815             public void write(int b) throws IOException {
 816                 byte[] a = new byte[]{(byte) b};
 817                 write(a, 0, 1);
 818             }
 819             @Override
 820             public void write(byte[] b, int off, int len) throws IOException {
 821                 NioSocketImpl.this.write(b, off, len);
 822             }
 823             @Override
 824             public void close() throws IOException {
 825                 NioSocketImpl.this.close();
 826             }
 827         };
 828     }
 829 
 830     @Override
 831     protected int available() throws IOException {
 832         synchronized (stateLock) {
 833             ensureOpenAndConnected();
 834             if (isInputClosed) {
 835                 return 0;
 836             } else {
 837                 return Net.available(fd);
 838             }
 839         }
 840     }
 841 
 842     /**
 843      * Closes the socket if there are no I/O operations in progress.
 844      */
 845     private boolean tryClose() throws IOException {
 846         assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
 847         if (readerThread == 0 && writerThread == 0) {
 848             try {
 849                 cleaner.clean();
 850             } catch (UncheckedIOException ioe) {
 851                 throw ioe.getCause();
 852             } finally {
 853                 state = ST_CLOSED;
 854             }
 855             return true;
 856         } else {
 857             return false;
 858         }
 859     }
 860 
 861     /**
 862      * Invokes tryClose to attempt to close the socket.
 863      *
 864      * This method is used for deferred closing by I/O operations.
 865      */
 866     private void tryFinishClose() {
 867         try {
 868             tryClose();
 869         } catch (IOException ignore) { }
 870     }
 871 
 872     /**
 873      * Closes the socket. If there are I/O operations in progress then the
 874      * socket is pre-closed and the threads are signalled. The socket will be
 875      * closed when the last I/O operation aborts.
 876      */
 877     @Override
 878     protected void close() throws IOException {
 879         synchronized (stateLock) {
 880             int state = this.state;
 881             if (state >= ST_CLOSING)
 882                 return;
 883             if (state == ST_NEW) {
 884                 // stillborn
 885                 this.state = ST_CLOSED;
 886                 return;
 887             }
 888             boolean connected = (state == ST_CONNECTED);
 889             this.state = ST_CLOSING;
 890 
 891             // shutdown output when linger interval not set to 0
 892             if (connected) {
 893                 try {
 894                     var SO_LINGER = StandardSocketOptions.SO_LINGER;
 895                     if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
 896                         Net.shutdown(fd, Net.SHUT_WR);
 897                     }
 898                 } catch (IOException ignore) { }
 899             }
 900 
 901             // attempt to close the socket. If there are I/O operations in progress
 902             // then the socket is pre-closed and the thread(s) signalled. The
 903             // last thread will close the file descriptor.
 904             if (!tryClose()) {
 905                 nd.preClose(fd, readerThread, writerThread);
 906             }
 907         }
 908     }
 909 
 910     // the socket options supported by client and server sockets
 911     private static volatile Set<SocketOption<?>> clientSocketOptions;
 912     private static volatile Set<SocketOption<?>> serverSocketOptions;
 913 
 914     @Override
 915     protected Set<SocketOption<?>> supportedOptions() {
 916         Set<SocketOption<?>> options = (server) ? serverSocketOptions : clientSocketOptions;
 917         if (options == null) {
 918             options = new HashSet<>();
 919             options.add(StandardSocketOptions.SO_RCVBUF);
 920             options.add(StandardSocketOptions.SO_REUSEADDR);
 921             if (server) {
 922                 // IP_TOS added for server socket to maintain compatibility
 923                 options.add(StandardSocketOptions.IP_TOS);
 924                 options.addAll(ExtendedSocketOptions.serverSocketOptions());
 925             } else {
 926                 options.add(StandardSocketOptions.IP_TOS);
 927                 options.add(StandardSocketOptions.SO_KEEPALIVE);
 928                 options.add(StandardSocketOptions.SO_SNDBUF);
 929                 options.add(StandardSocketOptions.SO_LINGER);
 930                 options.add(StandardSocketOptions.TCP_NODELAY);
 931                 options.addAll(ExtendedSocketOptions.clientSocketOptions());
 932             }
 933             if (Net.isReusePortAvailable())
 934                 options.add(StandardSocketOptions.SO_REUSEPORT);
 935             options = Collections.unmodifiableSet(options);
 936             if (server) {
 937                 serverSocketOptions = options;
 938             } else {
 939                 clientSocketOptions = options;
 940             }
 941         }
 942         return options;
 943     }
 944 
 945     @Override
 946     protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
 947         if (!supportedOptions().contains(opt))
 948             throw new UnsupportedOperationException("'" + opt + "' not supported");
 949         if (!opt.type().isInstance(value))
 950             throw new IllegalArgumentException("Invalid value '" + value + "'");
 951         synchronized (stateLock) {
 952             ensureOpen();
 953             if (opt == StandardSocketOptions.IP_TOS) {
 954                 // maps to IPV6_TCLASS and/or IP_TOS
 955                 Net.setIpSocketOption(fd, family(), opt, value);
 956             } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
 957                 boolean b = (boolean) value;
 958                 if (Net.useExclusiveBind()) {
 959                     isReuseAddress = b;
 960                 } else {
 961                     Net.setSocketOption(fd, opt, b);
 962                 }
 963             } else {
 964                 // option does not need special handling
 965                 Net.setSocketOption(fd, opt, value);
 966             }
 967         }
 968     }
 969 
 970     @SuppressWarnings("unchecked")
 971     protected <T> T getOption(SocketOption<T> opt) throws IOException {
 972         if (!supportedOptions().contains(opt))
 973             throw new UnsupportedOperationException("'" + opt + "' not supported");
 974         synchronized (stateLock) {
 975             ensureOpen();
 976             if (opt == StandardSocketOptions.IP_TOS) {
 977                 return (T) Net.getSocketOption(fd, family(), opt);
 978             } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
 979                 if (Net.useExclusiveBind()) {
 980                     return (T) Boolean.valueOf(isReuseAddress);
 981                 } else {
 982                     return (T) Net.getSocketOption(fd, opt);
 983                 }
 984             } else {
 985                 // option does not need special handling
 986                 return (T) Net.getSocketOption(fd, opt);
 987             }
 988         }
 989     }
 990 
 991     private boolean booleanValue(Object value, String desc) throws SocketException {
 992         if (!(value instanceof Boolean))
 993             throw new SocketException("Bad value for " + desc);
 994         return (boolean) value;
 995     }
 996 
 997     private int intValue(Object value, String desc) throws SocketException {
 998         if (!(value instanceof Integer))
 999             throw new SocketException("Bad value for " + desc);
1000         return (int) value;
1001     }
1002 
1003     @Override
1004     public void setOption(int opt, Object value) throws SocketException {
1005         synchronized (stateLock) {
1006             ensureOpen();
1007             try {
1008                 switch (opt) {
1009                 case SO_LINGER: {
1010                     // the value is "false" to disable, or linger interval to enable
1011                     int i;
1012                     if (value instanceof Boolean && ((boolean) value) == false) {
1013                         i = -1;
1014                     } else {
1015                         i = intValue(value, "SO_LINGER");
1016                     }
1017                     Net.setSocketOption(fd, StandardSocketOptions.SO_LINGER, i);
1018                     break;
1019                 }
1020                 case SO_TIMEOUT: {
1021                     int i = intValue(value, "SO_TIMEOUT");
1022                     if (i < 0)
1023                         throw new IllegalArgumentException("timeout < 0");
1024                     timeout = i;
1025                     break;
1026                 }
1027                 case IP_TOS: {
1028                     int i = intValue(value, "IP_TOS");
1029                     Net.setIpSocketOption(fd, family(), StandardSocketOptions.IP_TOS, i);
1030                     break;
1031                 }
1032                 case TCP_NODELAY: {
1033                     boolean b = booleanValue(value, "TCP_NODELAY");
1034                     Net.setSocketOption(fd, StandardSocketOptions.TCP_NODELAY, b);
1035                     break;
1036                 }
1037                 case SO_SNDBUF: {
1038                     int i = intValue(value, "SO_SNDBUF");
1039                     if (i <= 0)
1040                         throw new SocketException("SO_SNDBUF <= 0");
1041                     Net.setSocketOption(fd, StandardSocketOptions.SO_SNDBUF, i);
1042                     break;
1043                 }
1044                 case SO_RCVBUF: {
1045                     int i = intValue(value, "SO_RCVBUF");
1046                     if (i <= 0)
1047                         throw new SocketException("SO_RCVBUF <= 0");
1048                     Net.setSocketOption(fd, StandardSocketOptions.SO_RCVBUF, i);
1049                     break;
1050                 }
1051                 case SO_KEEPALIVE: {
1052                     boolean b = booleanValue(value, "SO_KEEPALIVE");
1053                     Net.setSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE, b);
1054                     break;
1055                 }
1056                 case SO_OOBINLINE: {
1057                     boolean b = booleanValue(value, "SO_OOBINLINE");
1058                     Net.setSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE, b);
1059                     break;
1060                 }
1061                 case SO_REUSEADDR: {
1062                     boolean b = booleanValue(value, "SO_REUSEADDR");
1063                     if (Net.useExclusiveBind()) {
1064                         isReuseAddress = b;
1065                     } else {
1066                         Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEADDR, b);
1067                     }
1068                     break;
1069                 }
1070                 case SO_REUSEPORT: {
1071                     if (!Net.isReusePortAvailable())
1072                         throw new SocketException("SO_REUSEPORT not supported");
1073                     boolean b = booleanValue(value, "SO_REUSEPORT");
1074                     Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEPORT, b);
1075                     break;
1076                 }
1077                 default:
1078                     throw new SocketException("Unknown option " + opt);
1079                 }
1080             } catch (SocketException e) {
1081                 throw e;
1082             } catch (IllegalArgumentException | IOException e) {
1083                 throw asSocketException(e);
1084             }
1085         }
1086     }
1087 
1088     @Override
1089     public Object getOption(int opt) throws SocketException {
1090         synchronized (stateLock) {
1091             ensureOpen();
1092             try {
1093                 switch (opt) {
1094                 case SO_TIMEOUT:
1095                     return timeout;
1096                 case TCP_NODELAY:
1097                     return Net.getSocketOption(fd, StandardSocketOptions.TCP_NODELAY);
1098                 case SO_OOBINLINE:
1099                     return Net.getSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE);
1100                 case SO_LINGER: {
1101                     // return "false" when disabled, linger interval when enabled
1102                     int i = (int) Net.getSocketOption(fd, StandardSocketOptions.SO_LINGER);
1103                     if (i == -1) {
1104                         return Boolean.FALSE;
1105                     } else {
1106                         return i;
1107                     }
1108                 }
1109                 case SO_REUSEADDR:
1110                     if (Net.useExclusiveBind()) {
1111                         return isReuseAddress;
1112                     } else {
1113                         return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEADDR);
1114                     }
1115                 case SO_BINDADDR:
1116                     return Net.localAddress(fd).getAddress();
1117                 case SO_SNDBUF:
1118                     return Net.getSocketOption(fd, StandardSocketOptions.SO_SNDBUF);
1119                 case SO_RCVBUF:
1120                     return Net.getSocketOption(fd, StandardSocketOptions.SO_RCVBUF);
1121                 case IP_TOS:
1122                     return Net.getSocketOption(fd, family(), StandardSocketOptions.IP_TOS);
1123                 case SO_KEEPALIVE:
1124                     return Net.getSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE);
1125                 case SO_REUSEPORT:
1126                     if (!Net.isReusePortAvailable())
1127                         throw new SocketException("SO_REUSEPORT not supported");
1128                     return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEPORT);
1129                 default:
1130                     throw new SocketException("Unknown option " + opt);
1131                 }
1132             } catch (SocketException e) {
1133                 throw e;
1134             } catch (IllegalArgumentException | IOException e) {
1135                 throw asSocketException(e);
1136             }
1137         }
1138     }
1139 
1140     @Override
1141     protected void shutdownInput() throws IOException {
1142         synchronized (stateLock) {
1143             ensureOpenAndConnected();
1144             if (!isInputClosed) {
1145                 Net.shutdown(fd, Net.SHUT_RD);
1146                 if (NativeThread.isVirtualThread(readerThread)) {
1147                     Poller.stopPoll(fdVal(fd), Net.POLLIN);
1148                 }
1149                 isInputClosed = true;
1150             }
1151         }
1152     }
1153 
1154     @Override
1155     protected void shutdownOutput() throws IOException {
1156         synchronized (stateLock) {
1157             ensureOpenAndConnected();
1158             if (!isOutputClosed) {
1159                 Net.shutdown(fd, Net.SHUT_WR);
1160                 if (NativeThread.isVirtualThread(writerThread)) {
1161                     Poller.stopPoll(fdVal(fd), Net.POLLOUT);
1162                 }
1163                 isOutputClosed = true;
1164             }
1165         }
1166     }
1167 
1168     @Override
1169     protected boolean supportsUrgentData() {
1170         return true;
1171     }
1172 
1173     @Override
1174     protected void sendUrgentData(int data) throws IOException {
1175         writeLock.lock();
1176         try {
1177             int n = 0;
1178             FileDescriptor fd = beginWrite();
1179             try {
1180                 configureNonBlockingIfNeeded(fd, false);
1181                 do {
1182                     n = Net.sendOOB(fd, (byte) data);
1183                 } while (n == IOStatus.INTERRUPTED && isOpen());
1184                 if (n == IOStatus.UNAVAILABLE) {
1185                     throw new SocketException("No buffer space available");
1186                 }
1187             } finally {
1188                 endWrite(n > 0);
1189             }
1190         } finally {
1191             writeLock.unlock();
1192         }
1193     }
1194 
1195     /**
1196      * Returns an action to close the given file descriptor.
1197      */
1198     private static Runnable closerFor(FileDescriptor fd) {
1199         return () -> {
1200             try {
1201                 nd.close(fd);
1202             } catch (IOException ioe) {
1203                 throw new UncheckedIOException(ioe);
1204             }
1205         };
1206     }
1207 
1208     /**
1209      * Attempts to acquire the given lock within the given waiting time.
1210      * @return the remaining time in nanoseconds when the lock is acquired, zero
1211      *         or less if the lock was not acquired before the timeout expired
1212      */
1213     private static long tryLock(ReentrantLock lock, long timeout, TimeUnit unit) {
1214         assert timeout > 0;
1215         boolean interrupted = false;
1216         long nanos = unit.toNanos(timeout);
1217         long remainingNanos = nanos;
1218         long startNanos = System.nanoTime();
1219         boolean acquired = false;
1220         while (!acquired && (remainingNanos > 0)) {
1221             try {
1222                 acquired = lock.tryLock(remainingNanos, NANOSECONDS);
1223             } catch (InterruptedException e) {
1224                 interrupted = true;
1225             }
1226             remainingNanos = nanos - (System.nanoTime() - startNanos);
1227         }
1228         if (acquired && remainingNanos <= 0L)
1229             lock.unlock();  // release lock if timeout has expired
1230         if (interrupted)
1231             Thread.currentThread().interrupt();
1232         return remainingNanos;
1233     }
1234 
1235     /**
1236      * Creates a SocketException from the given exception.
1237      */
1238     private static SocketException asSocketException(Exception e) {
1239         if (e instanceof SocketException se) {
1240             return se;
1241         } else {
1242             var se = new SocketException(e.getMessage());
1243             se.setStackTrace(e.getStackTrace());
1244             return se;
1245         }
1246     }
1247 
1248     /**
1249      * Returns the socket protocol family.
1250      */
1251     private static ProtocolFamily family() {
1252         if (Net.isIPv6Available()) {
1253             return StandardProtocolFamily.INET6;
1254         } else {
1255             return StandardProtocolFamily.INET;
1256         }
1257     }
1258 
1259     /**
1260      * Return the file descriptor value.
1261      */
1262     private static int fdVal(FileDescriptor fd) {
1263         return JIOFDA.get(fd);
1264     }
1265 
1266     private static final JavaIOFileDescriptorAccess JIOFDA = SharedSecrets.getJavaIOFileDescriptorAccess();
1267 }