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.NetHooks;
  59 import sun.net.PlatformSocketImpl;
  60 import sun.net.ext.ExtendedSocketOptions;
  61 import jdk.internal.util.Exceptions;
  62 
  63 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  64 import static java.util.concurrent.TimeUnit.NANOSECONDS;
  65 import static jdk.internal.util.Exceptions.filterNonSocketInfo;
  66 import static jdk.internal.util.Exceptions.formatMsg;
  67 
  68 /**
  69  * NIO based SocketImpl.
  70  *
  71  * The underlying socket used by this SocketImpl is initially configured blocking.
  72  * If a connect, accept or read is attempted with a timeout, or a virtual
  73  * thread invokes a blocking operation, then the socket is changed to non-blocking
  74  * When in non-blocking mode, operations that don't complete immediately will
  75  * poll the socket (or park when invoked on a virtual thread) and preserve
  76  * the semantics of blocking operations.
  77  */
  78 
  79 public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
  80     private static final NativeDispatcher nd = new SocketDispatcher();
  81 
  82     // The maximum number of bytes to read/write per syscall to avoid needing
  83     // a huge buffer from the temporary buffer cache
  84     private static final int MAX_BUFFER_SIZE = 128 * 1024;
  85 
  86     // true if this is a SocketImpl for a ServerSocket
  87     private final boolean server;
  88 
  89     // Lock held when reading (also used when accepting or connecting)
  90     private final ReentrantLock readLock = new ReentrantLock();
  91 
  92     // Lock held when writing
  93     private final ReentrantLock writeLock = new ReentrantLock();
  94 
  95     // The stateLock for read/changing state
  96     private final Object stateLock = new Object();
  97     private static final int ST_NEW = 0;
  98     private static final int ST_UNCONNECTED = 1;
  99     private static final int ST_CONNECTING = 2;
 100     private static final int ST_CONNECTED = 3;
 101     private static final int ST_CLOSING = 4;
 102     private static final int ST_CLOSED = 5;
 103     private volatile int state;  // need stateLock to change
 104 
 105     private Cleanable cleaner;
 106 
 107     // set to true when the socket is in non-blocking mode
 108     private volatile boolean nonBlocking;
 109 
 110     // used by connect/read/write/accept, protected by stateLock
 111     private long readerThread;
 112     private long writerThread;
 113 
 114     // used when SO_REUSEADDR is emulated, protected by stateLock
 115     private boolean isReuseAddress;
 116 
 117     // read or accept timeout in millis
 118     private volatile int timeout;
 119 
 120     // flags to indicate if the connection is shutdown for input and output
 121     private volatile boolean isInputClosed;
 122     private volatile boolean isOutputClosed;
 123 
 124     // used by read to emulate legacy behavior, protected by readLock
 125     private boolean readEOF;
 126     private boolean connectionReset;
 127 
 128     /**
 129      * Creates an instance of this SocketImpl.
 130      * @param server true if this is a SocketImpl for a ServerSocket
 131      */
 132     public NioSocketImpl(boolean server) {
 133         this.server = server;
 134     }
 135 
 136     /**
 137      * Returns true if the socket is open.
 138      */
 139     private boolean isOpen() {
 140         return state < ST_CLOSING;
 141     }
 142 
 143     /**
 144      * Throws SocketException if the socket is not open.
 145      */
 146     private void ensureOpen() throws SocketException {
 147         int state = this.state;
 148         if (state == ST_NEW)
 149             throw new SocketException("Socket not created");
 150         if (state >= ST_CLOSING)
 151             throw new SocketException("Socket closed");
 152     }
 153 
 154     /**
 155      * Throws SocketException if the socket is not open and connected.
 156      */
 157     private void ensureOpenAndConnected() throws SocketException {
 158         int state = this.state;
 159         if (state < ST_CONNECTED)
 160             throw new SocketException("Not connected");
 161         if (state > ST_CONNECTED)
 162             throw new SocketException("Socket closed");
 163     }
 164 
 165     /**
 166      * Disables the current thread for scheduling purposes until the
 167      * socket is ready for I/O or is asynchronously closed, for up to the
 168      * specified waiting time.
 169      * @throws IOException if an I/O error occurs
 170      */
 171     private void park(FileDescriptor fd, int event, long nanos) throws IOException {
 172         Thread t = Thread.currentThread();
 173         if (t.isVirtual()) {
 174             Poller.poll(fdVal(fd), event, nanos, this::isOpen);
 175             if (t.isInterrupted()) {
 176                 throw new InterruptedIOException();
 177             }
 178         } else {
 179             long millis;
 180             if (nanos == 0) {
 181                 millis = -1;
 182             } else {
 183                 millis = NANOSECONDS.toMillis(nanos);
 184                 if (nanos > MILLISECONDS.toNanos(millis)) {
 185                     // Round up any excess nanos to the nearest millisecond to
 186                     // avoid parking for less than requested.
 187                     millis++;
 188                 }
 189             }
 190             Net.poll(fd, event, millis);
 191         }
 192     }
 193 
 194     /**
 195      * Disables the current thread for scheduling purposes until the socket is
 196      * ready for I/O or is asynchronously closed.
 197      * @throws IOException if an I/O error occurs
 198      */
 199     private void park(FileDescriptor fd, int event) throws IOException {
 200         park(fd, event, 0);
 201     }
 202 
 203     /**
 204      * Ensures that the socket is configured non-blocking invoked on a virtual
 205      * thread or the operation has a timeout
 206      * @throws IOException if there is an I/O error changing the blocking mode
 207      */
 208     private void configureNonBlockingIfNeeded(FileDescriptor fd, boolean timed)
 209         throws IOException
 210     {
 211         if (!nonBlocking
 212             && (timed || Thread.currentThread().isVirtual())) {
 213             assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
 214             IOUtil.configureBlocking(fd, false);
 215             nonBlocking = true;
 216         }
 217     }
 218 
 219     /**
 220      * Marks the beginning of a read operation that might block.
 221      * @throws SocketException if the socket is closed or not connected
 222      */
 223     private FileDescriptor beginRead() throws SocketException {
 224         synchronized (stateLock) {
 225             ensureOpenAndConnected();
 226             readerThread = NativeThread.current();
 227             return fd;
 228         }
 229     }
 230 
 231     /**
 232      * Marks the end of a read operation that may have blocked.
 233      * @throws SocketException is the socket is closed
 234      */
 235     private void endRead(boolean completed) throws SocketException {
 236         synchronized (stateLock) {
 237             readerThread = 0;
 238             int state = this.state;
 239             if (state == ST_CLOSING)
 240                 tryFinishClose();
 241             if (!completed && state >= ST_CLOSING)
 242                 throw new SocketException("Socket closed");
 243         }
 244     }
 245 
 246     /**
 247      * Attempts to read bytes from the socket into the given byte array.
 248      */
 249     private int tryRead(FileDescriptor fd, byte[] b, int off, int len)
 250         throws IOException
 251     {
 252         ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
 253         assert dst.position() == 0;
 254         try {
 255             int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
 256             if (n > 0) {
 257                 dst.get(b, off, n);
 258             }
 259             return n;
 260         } finally {
 261             Util.offerFirstTemporaryDirectBuffer(dst);
 262         }
 263     }
 264 
 265     /**
 266      * Reads bytes from the socket into the given byte array with a timeout.
 267      * @throws SocketTimeoutException if the read timeout elapses
 268      */
 269     private int timedRead(FileDescriptor fd, byte[] b, int off, int len, long nanos)
 270         throws IOException
 271     {
 272         long startNanos = System.nanoTime();
 273         int n = tryRead(fd, b, off, len);
 274         while (n == IOStatus.UNAVAILABLE && isOpen()) {
 275             long remainingNanos = nanos - (System.nanoTime() - startNanos);
 276             if (remainingNanos <= 0) {
 277                 throw new SocketTimeoutException("Read timed out");
 278             }
 279             park(fd, Net.POLLIN, remainingNanos);
 280             n = tryRead(fd, b, off, len);
 281         }
 282         return n;
 283     }
 284 
 285     /**
 286      * Reads bytes from the socket into the given byte array.
 287      * @return the number of bytes read or -1 at EOF
 288      * @throws SocketException if the socket is closed or a socket I/O error occurs
 289      * @throws SocketTimeoutException if the read timeout elapses
 290      */
 291     private int implRead(byte[] b, int off, int len, long remainingNanos) throws IOException {
 292         int n = 0;
 293         FileDescriptor fd = beginRead();
 294         try {
 295             if (connectionReset)
 296                 throw new SocketException("Connection reset");
 297             if (isInputClosed)
 298                 return -1;
 299             configureNonBlockingIfNeeded(fd, remainingNanos > 0);
 300             if (remainingNanos > 0) {
 301                 // read with timeout
 302                 n = timedRead(fd, b, off, len, remainingNanos);
 303             } else {
 304                 // read, no timeout
 305                 n = tryRead(fd, b, off, len);
 306                 while (IOStatus.okayToRetry(n) && isOpen()) {
 307                     park(fd, Net.POLLIN);
 308                     n = tryRead(fd, b, off, len);
 309                 }
 310             }
 311             return n;
 312         } catch (InterruptedIOException e) {
 313             throw e;
 314         } catch (ConnectionResetException e) {
 315             connectionReset = true;
 316             throw new SocketException("Connection reset");
 317         } catch (IOException ioe) {
 318             // throw SocketException to maintain compatibility
 319             throw asSocketException(ioe);
 320         } finally {
 321             endRead(n > 0);
 322         }
 323     }
 324 
 325     /**
 326      * Reads bytes from the socket into the given byte array.
 327      * @return the number of bytes read or -1 at EOF
 328      * @throws IndexOutOfBoundsException if the bound checks fail
 329      * @throws SocketException if the socket is closed or a socket I/O error occurs
 330      * @throws SocketTimeoutException if the read timeout elapses
 331      */
 332     private int read(byte[] b, int off, int len) throws IOException {
 333         Objects.checkFromIndexSize(off, len, b.length);
 334         if (len == 0) {
 335             return 0;
 336         } else {
 337             long remainingNanos = 0;
 338             int timeout = this.timeout;
 339             if (timeout > 0) {
 340                 remainingNanos = tryLock(readLock, timeout, MILLISECONDS);
 341                 if (remainingNanos <= 0) {
 342                     assert !readLock.isHeldByCurrentThread();
 343                     throw new SocketTimeoutException("Read timed out");
 344                 }
 345             } else {
 346                 readLock.lock();
 347             }
 348             try {
 349                 // emulate legacy behavior to return -1, even if socket is closed
 350                 if (readEOF)
 351                     return -1;
 352                 // read up to MAX_BUFFER_SIZE bytes
 353                 int size = Math.min(len, MAX_BUFFER_SIZE);
 354                 int n = implRead(b, off, size, remainingNanos);
 355                 if (n == -1)
 356                     readEOF = true;
 357                 return n;
 358             } finally {
 359                 readLock.unlock();
 360             }
 361         }
 362     }
 363 
 364     /**
 365      * Marks the beginning of a write operation that might block.
 366      * @throws SocketException if the socket is closed or not connected
 367      */
 368     private FileDescriptor beginWrite() throws SocketException {
 369         synchronized (stateLock) {
 370             ensureOpenAndConnected();
 371             writerThread = NativeThread.current();
 372             return fd;
 373         }
 374     }
 375 
 376     /**
 377      * Marks the end of a write operation that may have blocked.
 378      * @throws SocketException is the socket is closed
 379      */
 380     private void endWrite(boolean completed) throws SocketException {
 381         synchronized (stateLock) {
 382             writerThread = 0;
 383             int state = this.state;
 384             if (state == ST_CLOSING)
 385                 tryFinishClose();
 386             if (!completed && state >= ST_CLOSING)
 387                 throw new SocketException("Socket closed");
 388         }
 389     }
 390 
 391     /**
 392      * Attempts to write a sequence of bytes to the socket from the given
 393      * byte array.
 394      */
 395     private int tryWrite(FileDescriptor fd, byte[] b, int off, int len)
 396         throws IOException
 397     {
 398         ByteBuffer src = Util.getTemporaryDirectBuffer(len);
 399         assert src.position() == 0;
 400         try {
 401             src.put(b, off, len);
 402             return nd.write(fd, ((DirectBuffer)src).address(), len);
 403         } finally {
 404             Util.offerFirstTemporaryDirectBuffer(src);
 405         }
 406     }
 407 
 408     /**
 409      * Writes a sequence of bytes to the socket from the given byte array.
 410      * @return the number of bytes written
 411      * @throws SocketException if the socket is closed or a socket I/O error occurs
 412      */
 413     private int implWrite(byte[] b, int off, int len) throws IOException {
 414         int n = 0;
 415         FileDescriptor fd = beginWrite();
 416         try {
 417             configureNonBlockingIfNeeded(fd, false);
 418             n = tryWrite(fd, b, off, len);
 419             while (IOStatus.okayToRetry(n) && isOpen()) {
 420                 park(fd, Net.POLLOUT);
 421                 n = tryWrite(fd, b, off, len);
 422             }
 423             return n;
 424         } catch (InterruptedIOException e) {
 425             throw e;
 426         } catch (IOException ioe) {
 427             // throw SocketException to maintain compatibility
 428             throw asSocketException(ioe);
 429         } finally {
 430             endWrite(n > 0);
 431         }
 432     }
 433 
 434     /**
 435      * Writes a sequence of bytes to the socket from the given byte array.
 436      * @throws SocketException if the socket is closed or a socket I/O error occurs
 437      */
 438     private void write(byte[] b, int off, int len) throws IOException {
 439         Objects.checkFromIndexSize(off, len, b.length);
 440         if (len > 0) {
 441             writeLock.lock();
 442             try {
 443                 int pos = off;
 444                 int end = off + len;
 445                 while (pos < end) {
 446                     // write up to MAX_BUFFER_SIZE bytes
 447                     int size = Math.min((end - pos), MAX_BUFFER_SIZE);
 448                     int n = implWrite(b, pos, size);
 449                     pos += n;
 450                 }
 451             } finally {
 452                 writeLock.unlock();
 453             }
 454         }
 455     }
 456 
 457     /**
 458      * Creates the socket.
 459      * @param stream {@code true} for a streams socket
 460      */
 461     @Override
 462     protected void create(boolean stream) throws IOException {
 463         if (!stream) {
 464             throw new IOException("Datagram socket creation not supported");
 465         }
 466         synchronized (stateLock) {
 467             if (state != ST_NEW)
 468                 throw new IOException("Already created");
 469             FileDescriptor fd;
 470             if (server) {
 471                 fd = Net.serverSocket();
 472             } else {
 473                 fd = Net.socket();
 474             }
 475             Runnable closer = closerFor(fd);
 476             this.fd = fd;
 477             this.cleaner = CleanerFactory.cleaner().register(this, closer);
 478             this.state = ST_UNCONNECTED;
 479         }
 480     }
 481 
 482     /**
 483      * Marks the beginning of a connect operation that might block.
 484      * @throws SocketException if the socket is closed or already connected
 485      */
 486     private FileDescriptor beginConnect(InetAddress address, int port)
 487         throws IOException
 488     {
 489         synchronized (stateLock) {
 490             int state = this.state;
 491             if (state != ST_UNCONNECTED) {
 492                 if (state == ST_NEW)
 493                     throw new SocketException("Not created");
 494                 if (state == ST_CONNECTING)
 495                     throw new SocketException("Connection in progress");
 496                 if (state == ST_CONNECTED)
 497                     throw new SocketException("Already connected");
 498                 if (state >= ST_CLOSING)
 499                     throw new SocketException("Socket closed");
 500                 assert false;
 501             }
 502             this.state = ST_CONNECTING;
 503 
 504             // invoke beforeTcpConnect hook if not already bound
 505             if (localport == 0) {
 506                 NetHooks.beforeTcpConnect(fd, address, port);
 507             }
 508 
 509             // save the remote address/port
 510             this.address = address;
 511             this.port = port;
 512 
 513             readerThread = NativeThread.current();
 514             return fd;
 515         }
 516     }
 517 
 518     /**
 519      * Marks the end of a connect operation that may have blocked.
 520      * @throws SocketException is the socket is closed
 521      */
 522     private void endConnect(FileDescriptor fd, boolean completed) throws IOException {
 523         synchronized (stateLock) {
 524             readerThread = 0;
 525             int state = this.state;
 526             if (state == ST_CLOSING)
 527                 tryFinishClose();
 528             if (completed && state == ST_CONNECTING) {
 529                 this.state = ST_CONNECTED;
 530                 localport = Net.localAddress(fd).getPort();
 531             } else if (!completed && state >= ST_CLOSING) {
 532                 throw new SocketException("Socket closed");
 533             }
 534         }
 535     }
 536 
 537     /**
 538      * Waits for a connection attempt to finish with a timeout
 539      * @throws SocketTimeoutException if the connect timeout elapses
 540      */
 541     private boolean timedFinishConnect(FileDescriptor fd, long nanos) throws IOException {
 542         long startNanos = System.nanoTime();
 543         boolean polled = Net.pollConnectNow(fd);
 544         while (!polled && isOpen()) {
 545             long remainingNanos = nanos - (System.nanoTime() - startNanos);
 546             if (remainingNanos <= 0) {
 547                 throw new SocketTimeoutException("Connect timed out");
 548             }
 549             park(fd, Net.POLLOUT, remainingNanos);
 550             polled = Net.pollConnectNow(fd);
 551         }
 552         return polled && isOpen();
 553     }
 554 
 555     /**
 556      * Attempts to establish a connection to the given socket address with a
 557      * timeout. Closes the socket if connection cannot be established.
 558      * @throws IOException if the address is not a resolved InetSocketAddress or
 559      *         the connection cannot be established
 560      */
 561     @Override
 562     protected void connect(SocketAddress remote, int millis) throws IOException {
 563         // SocketImpl connect only specifies IOException
 564         if (!(remote instanceof InetSocketAddress))
 565             throw new IOException("Unsupported address type");
 566         InetSocketAddress isa = (InetSocketAddress) remote;
 567         if (isa.isUnresolved()) {
 568             throw new UnknownHostException(
 569                 formatMsg(filterNonSocketInfo(isa.getHostName())));
 570         }
 571 
 572         InetAddress address = isa.getAddress();
 573         if (address.isAnyLocalAddress())
 574             address = InetAddress.getLocalHost();
 575         int port = isa.getPort();
 576 
 577         ReentrantLock connectLock = readLock;
 578         try {
 579             connectLock.lock();
 580             try {
 581                 boolean connected = false;
 582                 FileDescriptor fd = beginConnect(address, port);
 583                 try {
 584                     configureNonBlockingIfNeeded(fd, millis > 0);
 585                     int n = Net.connect(fd, address, port);
 586                     if (n > 0) {
 587                         // connection established
 588                         connected = true;
 589                     } else {
 590                         assert IOStatus.okayToRetry(n);
 591                         if (millis > 0) {
 592                             // finish connect with timeout
 593                             long nanos = MILLISECONDS.toNanos(millis);
 594                             connected = timedFinishConnect(fd, nanos);
 595                         } else {
 596                             // finish connect, no timeout
 597                             boolean polled = false;
 598                             while (!polled && isOpen()) {
 599                                 park(fd, Net.POLLOUT);
 600                                 polled = Net.pollConnectNow(fd);
 601                             }
 602                             connected = polled && isOpen();
 603                         }
 604                     }
 605                 } finally {
 606                     endConnect(fd, connected);
 607                 }
 608             } finally {
 609                 connectLock.unlock();
 610             }
 611         } catch (IOException ioe) {
 612             close();
 613             if (ioe instanceof SocketTimeoutException) {
 614                 throw ioe;
 615             } else if (ioe instanceof InterruptedIOException) {
 616                 assert Thread.currentThread().isVirtual();
 617                 throw new SocketException("Closed by interrupt");
 618             } else {
 619                 throw Exceptions.ioException(ioe, isa);
 620             }
 621         }
 622     }
 623 
 624     @Override
 625     protected void connect(String host, int port) throws IOException {
 626         connect(new InetSocketAddress(host, port), 0);
 627     }
 628 
 629     @Override
 630     protected void connect(InetAddress address, int port) throws IOException {
 631         connect(new InetSocketAddress(address, port), 0);
 632     }
 633 
 634     @Override
 635     protected void bind(InetAddress host, int port) throws IOException {
 636         synchronized (stateLock) {
 637             ensureOpen();
 638             if (localport != 0)
 639                 throw new SocketException("Already bound");
 640             NetHooks.beforeTcpBind(fd, host, port);
 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 }