1 /*
   2  * Copyright (c) 2000, 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.net.InetAddress;
  31 import java.net.Inet4Address;
  32 import java.net.InetSocketAddress;
  33 import java.net.ProtocolFamily;
  34 import java.net.Socket;
  35 import java.net.SocketAddress;
  36 import java.net.SocketException;
  37 import java.net.SocketOption;
  38 import java.net.SocketTimeoutException;
  39 import java.net.StandardSocketOptions;
  40 import java.nio.ByteBuffer;
  41 import java.nio.channels.AlreadyBoundException;
  42 import java.nio.channels.AlreadyConnectedException;
  43 import java.nio.channels.AsynchronousCloseException;
  44 import java.nio.channels.ClosedChannelException;
  45 import java.nio.channels.ConnectionPendingException;
  46 import java.nio.channels.IllegalBlockingModeException;
  47 import java.nio.channels.NoConnectionPendingException;
  48 import java.nio.channels.NotYetConnectedException;
  49 import java.nio.channels.SelectionKey;
  50 import java.nio.channels.SocketChannel;
  51 import java.nio.channels.spi.SelectorProvider;
  52 import java.nio.file.Path;
  53 import java.util.Collections;
  54 import java.util.HashSet;
  55 import java.util.Set;
  56 import java.util.Objects;
  57 import java.util.concurrent.locks.ReentrantLock;
  58 import static java.net.StandardProtocolFamily.INET;
  59 import static java.net.StandardProtocolFamily.INET6;
  60 import static java.net.StandardProtocolFamily.UNIX;
  61 
  62 import jdk.internal.event.SocketReadEvent;
  63 import jdk.internal.event.SocketWriteEvent;
  64 import sun.net.ConnectionResetException;
  65 import sun.net.ext.ExtendedSocketOptions;
  66 import jdk.internal.util.Exceptions;
  67 
  68 /**
  69  * An implementation of SocketChannels
  70  */
  71 
  72 class SocketChannelImpl
  73     extends SocketChannel
  74     implements SelChImpl
  75 {
  76     // Used to make native read and write calls
  77     private static final NativeDispatcher nd = new SocketDispatcher();
  78 
  79     // The protocol family of the socket
  80     private final ProtocolFamily family;
  81 
  82     // Our file descriptor object
  83     private final FileDescriptor fd;
  84     private final int fdVal;
  85 
  86     // Lock held by current reading or connecting thread
  87     private final ReentrantLock readLock = new ReentrantLock();
  88 
  89     // Lock held by current writing or connecting thread
  90     private final ReentrantLock writeLock = new ReentrantLock();
  91 
  92     // Lock held by any thread that modifies the state fields declared below
  93     // DO NOT invoke a blocking I/O operation while holding this lock!
  94     private final Object stateLock = new Object();
  95 
  96     // Input/Output closed
  97     private volatile boolean isInputClosed;
  98     private volatile boolean isOutputClosed;
  99 
 100     // Connection reset protected by readLock
 101     private boolean connectionReset;
 102 
 103     // -- The following fields are protected by stateLock
 104 
 105     // set true when exclusive binding is on and SO_REUSEADDR is emulated
 106     private boolean isReuseAddress;
 107 
 108     // State, increases monotonically
 109     private static final int ST_UNCONNECTED = 0;
 110     private static final int ST_CONNECTIONPENDING = 1;
 111     private static final int ST_CONNECTED = 2;
 112     private static final int ST_CLOSING = 3;
 113     private static final int ST_CLOSED = 4;
 114     private volatile int state;  // need stateLock to change
 115 
 116     // IDs of native threads doing reads and writes, for signalling
 117     private long readerThread;
 118     private long writerThread;
 119 
 120     // Binding
 121     private SocketAddress localAddress;
 122     private SocketAddress remoteAddress;
 123 
 124     // Socket adaptor, created on demand
 125     private Socket socket;
 126 
 127     // True if the channel's socket has been forced into non-blocking mode
 128     // by a virtual thread. It cannot be reset. When the channel is in
 129     // blocking mode and the channel's socket is in non-blocking mode then
 130     // operations that don't complete immediately will poll the socket and
 131     // preserve the semantics of blocking operations.
 132     private volatile boolean forcedNonBlocking;
 133 
 134     // -- End of fields protected by stateLock
 135 
 136     SocketChannelImpl(SelectorProvider sp) throws IOException {
 137         this(sp, Net.isIPv6Available() ? INET6 : INET);
 138     }
 139 
 140     SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException {
 141         super(sp);
 142         Objects.requireNonNull(family, "'family' is null");
 143         if ((family != INET) && (family != INET6) && (family != UNIX)) {
 144             throw new UnsupportedOperationException("Protocol family not supported");
 145         }
 146         if (family == INET6 && !Net.isIPv6Available()) {
 147             throw new UnsupportedOperationException("IPv6 not available");
 148         }
 149 
 150         this.family = family;
 151         if (family == UNIX) {
 152             this.fd = UnixDomainSockets.socket();
 153         } else {
 154             this.fd = Net.socket(family, true);
 155         }
 156         this.fdVal = IOUtil.fdVal(fd);
 157     }
 158 
 159     // Constructor for sockets obtained from server sockets
 160     //
 161     SocketChannelImpl(SelectorProvider sp,
 162                       ProtocolFamily family,
 163                       FileDescriptor fd,
 164                       SocketAddress remoteAddress)
 165         throws IOException
 166     {
 167         super(sp);
 168         this.family = family;
 169         this.fd = fd;
 170         this.fdVal = IOUtil.fdVal(fd);
 171         synchronized (stateLock) {
 172             if (family == UNIX) {
 173                 this.localAddress = UnixDomainSockets.localAddress(fd);
 174             } else {
 175                 this.localAddress = Net.localAddress(fd);
 176             }
 177             this.remoteAddress = remoteAddress;
 178             this.state = ST_CONNECTED;
 179         }
 180     }
 181 
 182     /**
 183      * Returns true if this channel is to a INET or INET6 socket.
 184      */
 185     boolean isNetSocket() {
 186         return (family == INET) || (family == INET6);
 187     }
 188 
 189     /**
 190      * Returns true if this channel is to a UNIX socket.
 191      */
 192     boolean isUnixSocket() {
 193         return (family == UNIX);
 194     }
 195 
 196     /**
 197      * Checks that the channel is open.
 198      *
 199      * @throws ClosedChannelException if channel is closed (or closing)
 200      */
 201     private void ensureOpen() throws ClosedChannelException {
 202         if (!isOpen())
 203             throw new ClosedChannelException();
 204     }
 205 
 206     /**
 207      * Checks that the channel is open and connected.
 208      *
 209      * @apiNote This method uses the "state" field to check if the channel is
 210      * open. It should never be used in conjunction with isOpen or ensureOpen
 211      * as these methods check AbstractInterruptibleChannel's closed field - that
 212      * field is set before implCloseSelectableChannel is called and so before
 213      * the state is changed.
 214      *
 215      * @throws ClosedChannelException if channel is closed (or closing)
 216      * @throws NotYetConnectedException if open and not connected
 217      */
 218     private void ensureOpenAndConnected() throws ClosedChannelException {
 219         int state = this.state;
 220         if (state < ST_CONNECTED) {
 221             throw new NotYetConnectedException();
 222         } else if (state > ST_CONNECTED) {
 223             throw new ClosedChannelException();
 224         }
 225     }
 226 
 227     @Override
 228     public Socket socket() {
 229         synchronized (stateLock) {
 230             if (socket == null) {
 231                 if (isNetSocket()) {
 232                     socket = SocketAdaptor.create(this);
 233                 } else {
 234                     throw new UnsupportedOperationException("Not supported");
 235                 }
 236             }
 237             return socket;
 238         }
 239     }
 240 
 241     @Override
 242     public SocketAddress getLocalAddress() throws IOException {
 243         synchronized (stateLock) {
 244             ensureOpen();
 245             return localAddress;
 246         }
 247     }
 248 
 249     @Override
 250     public SocketAddress getRemoteAddress() throws IOException {
 251         synchronized (stateLock) {
 252             ensureOpen();
 253             return remoteAddress;
 254         }
 255     }
 256 
 257     @Override
 258     public <T> SocketChannel setOption(SocketOption<T> name, T value)
 259         throws IOException
 260     {
 261         Objects.requireNonNull(name);
 262         if (!supportedOptions().contains(name))
 263             throw new UnsupportedOperationException("'" + name + "' not supported");
 264         if (!name.type().isInstance(value))
 265             throw new IllegalArgumentException("Invalid value '" + value + "'");
 266 
 267         synchronized (stateLock) {
 268             ensureOpen();
 269 
 270             if (isNetSocket()) {
 271                 if (name == StandardSocketOptions.IP_TOS) {
 272                     // maps to IPV6_TCLASS and/or IP_TOS
 273                     Net.setIpSocketOption(fd, family, name, value);
 274                     return this;
 275                 }
 276                 if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
 277                     // SO_REUSEADDR emulated when using exclusive bind
 278                     isReuseAddress = (Boolean) value;
 279                     return this;
 280                 }
 281             }
 282 
 283             // no options that require special handling
 284             Net.setSocketOption(fd, name, value);
 285             return this;
 286         }
 287     }
 288 
 289     @Override
 290     @SuppressWarnings("unchecked")
 291     public <T> T getOption(SocketOption<T> name)
 292         throws IOException
 293     {
 294         Objects.requireNonNull(name);
 295         if (!supportedOptions().contains(name))
 296             throw new UnsupportedOperationException("'" + name + "' not supported");
 297 
 298         synchronized (stateLock) {
 299             ensureOpen();
 300 
 301             if (isNetSocket()) {
 302                 if (name == StandardSocketOptions.IP_TOS) {
 303                     // special handling for IP_TOS
 304                     return (T) Net.getSocketOption(fd, family, name);
 305                 }
 306                 if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
 307                     // SO_REUSEADDR emulated when using exclusive bind
 308                     return (T) Boolean.valueOf(isReuseAddress);
 309                 }
 310             }
 311 
 312             // no options that require special handling
 313             return (T) Net.getSocketOption(fd, name);
 314         }
 315     }
 316 
 317     private static class DefaultOptionsHolder {
 318         static final Set<SocketOption<?>> defaultInetOptions = defaultInetOptions();
 319         static final Set<SocketOption<?>> defaultUnixOptions = defaultUnixOptions();
 320 
 321         private static Set<SocketOption<?>> defaultInetOptions() {
 322             HashSet<SocketOption<?>> set = new HashSet<>();
 323             set.add(StandardSocketOptions.SO_SNDBUF);
 324             set.add(StandardSocketOptions.SO_RCVBUF);
 325             set.add(StandardSocketOptions.SO_KEEPALIVE);
 326             set.add(StandardSocketOptions.SO_REUSEADDR);
 327             if (Net.isReusePortAvailable()) {
 328                 set.add(StandardSocketOptions.SO_REUSEPORT);
 329             }
 330             set.add(StandardSocketOptions.SO_LINGER);
 331             set.add(StandardSocketOptions.TCP_NODELAY);
 332             // additional options required by socket adaptor
 333             set.add(StandardSocketOptions.IP_TOS);
 334             set.add(ExtendedSocketOption.SO_OOBINLINE);
 335             set.addAll(ExtendedSocketOptions.clientSocketOptions());
 336             return Collections.unmodifiableSet(set);
 337         }
 338 
 339         private static Set<SocketOption<?>> defaultUnixOptions() {
 340             HashSet<SocketOption<?>> set = new HashSet<>();
 341             set.add(StandardSocketOptions.SO_SNDBUF);
 342             set.add(StandardSocketOptions.SO_RCVBUF);
 343             set.add(StandardSocketOptions.SO_LINGER);
 344             set.addAll(ExtendedSocketOptions.unixDomainSocketOptions());
 345             return Collections.unmodifiableSet(set);
 346         }
 347     }
 348 
 349     @Override
 350     public final Set<SocketOption<?>> supportedOptions() {
 351         if (isUnixSocket()) {
 352             return DefaultOptionsHolder.defaultUnixOptions;
 353         } else {
 354             return DefaultOptionsHolder.defaultInetOptions;
 355         }
 356     }
 357 
 358     /**
 359      * Marks the beginning of a read operation that might block.
 360      *
 361      * @throws ClosedChannelException if blocking and the channel is closed
 362      */
 363     private void beginRead(boolean blocking) throws ClosedChannelException {
 364         if (blocking) {
 365             // set hook for Thread.interrupt
 366             begin();
 367 
 368             synchronized (stateLock) {
 369                 ensureOpen();
 370                 // record thread so it can be signalled if needed
 371                 readerThread = NativeThread.current();
 372             }
 373         }
 374     }
 375 
 376     /**
 377      * Marks the end of a read operation that may have blocked.
 378      *
 379      * @throws AsynchronousCloseException if the channel was closed due to this
 380      * thread being interrupted on a blocking read operation.
 381      */
 382     private void endRead(boolean blocking, boolean completed)
 383         throws AsynchronousCloseException
 384     {
 385         if (blocking) {
 386             synchronized (stateLock) {
 387                 readerThread = 0;
 388                 if (state == ST_CLOSING) {
 389                     tryFinishClose();
 390                 }
 391             }
 392             // remove hook for Thread.interrupt
 393             end(completed);
 394         }
 395     }
 396 
 397     private void throwConnectionReset() throws SocketException {
 398         throw new SocketException("Connection reset");
 399     }
 400 
 401     private int implRead(ByteBuffer buf) throws IOException {
 402         Objects.requireNonNull(buf);
 403 
 404         readLock.lock();
 405         try {
 406             ensureOpenAndConnected();
 407             boolean blocking = isBlocking();
 408             int n = 0;
 409             try {
 410                 beginRead(blocking);
 411 
 412                 // check if connection has been reset
 413                 if (connectionReset)
 414                     throwConnectionReset();
 415 
 416                 // check if input is shutdown
 417                 if (isInputClosed)
 418                     return IOStatus.EOF;
 419 
 420                 configureSocketNonBlockingIfVirtualThread();
 421                 n = IOUtil.read(fd, buf, -1, nd);
 422                 if (blocking) {
 423                     while (IOStatus.okayToRetry(n) && isOpen()) {
 424                         park(Net.POLLIN);
 425                         n = IOUtil.read(fd, buf, -1, nd);
 426                     }
 427                 }
 428             } catch (ConnectionResetException e) {
 429                 connectionReset = true;
 430                 throwConnectionReset();
 431             } finally {
 432                 endRead(blocking, n > 0);
 433                 if (n <= 0 && isInputClosed)
 434                     return IOStatus.EOF;
 435             }
 436             return IOStatus.normalize(n);
 437         } finally {
 438             readLock.unlock();
 439         }
 440     }
 441 
 442     private long implRead(ByteBuffer[] dsts, int offset, int length)
 443         throws IOException
 444     {
 445         Objects.checkFromIndexSize(offset, length, dsts.length);
 446 
 447         readLock.lock();
 448         try {
 449             ensureOpenAndConnected();
 450             boolean blocking = isBlocking();
 451             long n = 0;
 452             try {
 453                 beginRead(blocking);
 454 
 455                 // check if connection has been reset
 456                 if (connectionReset)
 457                     throwConnectionReset();
 458 
 459                 // check if input is shutdown
 460                 if (isInputClosed)
 461                     return IOStatus.EOF;
 462 
 463                 configureSocketNonBlockingIfVirtualThread();
 464                 n = IOUtil.read(fd, dsts, offset, length, nd);
 465                 if (blocking) {
 466                     while (IOStatus.okayToRetry(n) && isOpen()) {
 467                         park(Net.POLLIN);
 468                         n = IOUtil.read(fd, dsts, offset, length, nd);
 469                     }
 470                 }
 471             } catch (ConnectionResetException e) {
 472                 connectionReset = true;
 473                 throwConnectionReset();
 474             } finally {
 475                 endRead(blocking, n > 0);
 476                 if (n <= 0 && isInputClosed)
 477                     return IOStatus.EOF;
 478             }
 479             return IOStatus.normalize(n);
 480         } finally {
 481             readLock.unlock();
 482         }
 483     }
 484 
 485     @Override
 486     public int read(ByteBuffer buf) throws IOException {
 487         if (!SocketReadEvent.enabled()) {
 488             return implRead(buf);
 489         }
 490         long start = SocketReadEvent.timestamp();
 491         int nbytes = implRead(buf);
 492         SocketReadEvent.offer(start, nbytes, remoteAddress(), 0);
 493         return nbytes;
 494     }
 495 
 496 
 497     @Override
 498     public long read(ByteBuffer[] dsts, int offset, int length)
 499         throws IOException
 500     {
 501         if (!SocketReadEvent.enabled()) {
 502             return implRead(dsts, offset, length);
 503         }
 504         long start = SocketReadEvent.timestamp();
 505         long nbytes = implRead(dsts, offset, length);
 506         SocketReadEvent.offer(start, nbytes, remoteAddress(), 0);
 507         return nbytes;
 508     }
 509 
 510     /**
 511      * Marks the beginning of a write operation that might block.
 512      *
 513      * @throws ClosedChannelException if blocking and the channel is closed
 514      */
 515     private void beginWrite(boolean blocking) throws ClosedChannelException {
 516         if (blocking) {
 517             // set hook for Thread.interrupt
 518             begin();
 519 
 520             synchronized (stateLock) {
 521                 ensureOpen();
 522                 if (isOutputClosed)
 523                     throw new ClosedChannelException();
 524                 // record thread so it can be signalled if needed
 525                 writerThread = NativeThread.current();
 526             }
 527         }
 528     }
 529 
 530     /**
 531      * Marks the end of a write operation that may have blocked.
 532      *
 533      * @throws AsynchronousCloseException if the channel was closed due to this
 534      * thread being interrupted on a blocking write operation.
 535      */
 536     private void endWrite(boolean blocking, boolean completed)
 537         throws AsynchronousCloseException
 538     {
 539         if (blocking) {
 540             synchronized (stateLock) {
 541                 writerThread = 0;
 542                 if (state == ST_CLOSING) {
 543                     tryFinishClose();
 544                 }
 545             }
 546             // remove hook for Thread.interrupt
 547             end(completed);
 548         }
 549     }
 550 
 551     private int implWrite(ByteBuffer buf) throws IOException {
 552         Objects.requireNonNull(buf);
 553         writeLock.lock();
 554         try {
 555             ensureOpenAndConnected();
 556             boolean blocking = isBlocking();
 557             int n = 0;
 558             try {
 559                 beginWrite(blocking);
 560                 configureSocketNonBlockingIfVirtualThread();
 561                 n = IOUtil.write(fd, buf, -1, nd);
 562                 if (blocking) {
 563                     while (IOStatus.okayToRetry(n) && isOpen()) {
 564                         park(Net.POLLOUT);
 565                         n = IOUtil.write(fd, buf, -1, nd);
 566                     }
 567                 }
 568             } finally {
 569                 endWrite(blocking, n > 0);
 570                 if (n <= 0 && isOutputClosed)
 571                     throw new AsynchronousCloseException();
 572             }
 573             return IOStatus.normalize(n);
 574         } finally {
 575             writeLock.unlock();
 576         }
 577     }
 578 
 579     private long implWrite(ByteBuffer[] srcs, int offset, int length)
 580         throws IOException
 581     {
 582         Objects.checkFromIndexSize(offset, length, srcs.length);
 583 
 584         writeLock.lock();
 585         try {
 586             ensureOpenAndConnected();
 587             boolean blocking = isBlocking();
 588             long n = 0;
 589             try {
 590                 beginWrite(blocking);
 591                 configureSocketNonBlockingIfVirtualThread();
 592                 n = IOUtil.write(fd, srcs, offset, length, nd);
 593                 if (blocking) {
 594                     while (IOStatus.okayToRetry(n) && isOpen()) {
 595                         park(Net.POLLOUT);
 596                         n = IOUtil.write(fd, srcs, offset, length, nd);
 597                     }
 598                 }
 599             } finally {
 600                 endWrite(blocking, n > 0);
 601                 if (n <= 0 && isOutputClosed)
 602                     throw new AsynchronousCloseException();
 603             }
 604             return IOStatus.normalize(n);
 605         } finally {
 606             writeLock.unlock();
 607         }
 608     }
 609 
 610     @Override
 611     public int write(ByteBuffer buf) throws IOException {
 612         if (!SocketWriteEvent.enabled()) {
 613             return implWrite(buf);
 614         }
 615         long start = SocketWriteEvent.timestamp();
 616         int nbytes = implWrite(buf);
 617         SocketWriteEvent.offer(start, nbytes, remoteAddress());
 618         return nbytes;
 619     }
 620 
 621     @Override
 622     public long write(ByteBuffer[] srcs, int offset, int length)
 623         throws IOException
 624     {
 625         if (!SocketWriteEvent.enabled()) {
 626             return implWrite(srcs, offset, length);
 627         }
 628         long start = SocketWriteEvent.timestamp();
 629         long nbytes = implWrite(srcs, offset, length);
 630         SocketWriteEvent.offer(start, nbytes, remoteAddress());
 631         return nbytes;
 632     }
 633 
 634     /**
 635      * Writes a byte of out of band data.
 636      */
 637     int sendOutOfBandData(byte b) throws IOException {
 638         writeLock.lock();
 639         try {
 640             ensureOpenAndConnected();
 641             boolean blocking = isBlocking();
 642             int n = 0;
 643             try {
 644                 beginWrite(blocking);
 645                 configureSocketNonBlockingIfVirtualThread();
 646                 do {
 647                     n = Net.sendOOB(fd, b);
 648                 } while (n == IOStatus.INTERRUPTED && isOpen());
 649                 if (blocking && n == IOStatus.UNAVAILABLE) {
 650                     throw new SocketException("No buffer space available");
 651                 }
 652             } finally {
 653                 endWrite(blocking, n > 0);
 654                 if (n <= 0 && isOutputClosed)
 655                     throw new AsynchronousCloseException();
 656             }
 657             return IOStatus.normalize(n);
 658         } finally {
 659             writeLock.unlock();
 660         }
 661     }
 662 
 663     /**
 664      * Marks the beginning of a transfer to this channel.
 665      * @throws ClosedChannelException if channel is closed or the output is shutdown
 666      * @throws NotYetConnectedException if open and not connected
 667      */
 668     void beforeTransferTo() throws ClosedChannelException {
 669         boolean completed = false;
 670         writeLock.lock();
 671         try {
 672             synchronized (stateLock) {
 673                 ensureOpenAndConnected();
 674                 if (isOutputClosed)
 675                     throw new ClosedChannelException();
 676                 writerThread = NativeThread.current();
 677                 completed = true;
 678             }
 679         } finally {
 680             if (!completed) {
 681                 writeLock.unlock();
 682             }
 683         }
 684     }
 685 
 686     /**
 687      * Marks the end of a transfer to this channel.
 688      * @throws AsynchronousCloseException if not completed and the channel is closed
 689      */
 690     void afterTransferTo(boolean completed) throws AsynchronousCloseException {
 691         synchronized (stateLock) {
 692             writerThread = 0;
 693             if (state == ST_CLOSING) {
 694                 tryFinishClose();
 695             }
 696         }
 697         writeLock.unlock();
 698         if (!completed && !isOpen()) {
 699             throw new AsynchronousCloseException();
 700         }
 701     }
 702 
 703     @Override
 704     protected void implConfigureBlocking(boolean block) throws IOException {
 705         readLock.lock();
 706         try {
 707             writeLock.lock();
 708             try {
 709                 lockedConfigureBlocking(block);
 710             } finally {
 711                 writeLock.unlock();
 712             }
 713         } finally {
 714             readLock.unlock();
 715         }
 716     }
 717 
 718     /**
 719      * Adjust the blocking mode while holding readLock or writeLock.
 720      */
 721     private void lockedConfigureBlocking(boolean block) throws IOException {
 722         assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
 723         synchronized (stateLock) {
 724             ensureOpen();
 725             // do nothing if virtual thread has forced the socket to be non-blocking
 726             if (!forcedNonBlocking) {
 727                 IOUtil.configureBlocking(fd, block);
 728             }
 729         }
 730     }
 731 
 732     /**
 733      * Attempts to adjust the blocking mode if the channel is open.
 734      * @return {@code true} if the blocking mode was adjusted
 735      */
 736     private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
 737         assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
 738         synchronized (stateLock) {
 739             // do nothing if virtual thread has forced the socket to be non-blocking
 740             if (!forcedNonBlocking && isOpen()) {
 741                 IOUtil.configureBlocking(fd, block);
 742                 return true;
 743             } else {
 744                 return false;
 745             }
 746         }
 747     }
 748 
 749     /**
 750      * Ensures that the socket is configured non-blocking when on a virtual thread.
 751      */
 752     private void configureSocketNonBlockingIfVirtualThread() throws IOException {
 753         assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
 754         if (!forcedNonBlocking && Thread.currentThread().isVirtual()) {
 755             synchronized (stateLock) {
 756                 ensureOpen();
 757                 IOUtil.configureBlocking(fd, false);
 758                 forcedNonBlocking = true;
 759             }
 760         }
 761     }
 762 
 763     /**
 764      * Returns the local address, or null if not bound
 765      */
 766     SocketAddress localAddress() {
 767         synchronized (stateLock) {
 768             return localAddress;
 769         }
 770     }
 771 
 772     /**
 773      * Returns the remote address, or null if not connected
 774      */
 775     SocketAddress remoteAddress() {
 776         synchronized (stateLock) {
 777             return remoteAddress;
 778         }
 779     }
 780 
 781     @Override
 782     public SocketChannel bind(SocketAddress local) throws IOException {
 783         readLock.lock();
 784         try {
 785             writeLock.lock();
 786             try {
 787                 synchronized (stateLock) {
 788                     ensureOpen();
 789                     if (state == ST_CONNECTIONPENDING)
 790                         throw new ConnectionPendingException();
 791                     if (localAddress != null)
 792                         throw new AlreadyBoundException();
 793                     if (isUnixSocket()) {
 794                         localAddress = unixBind(local);
 795                     } else {
 796                         localAddress = netBind(local);
 797                     }
 798                 }
 799             } finally {
 800                 writeLock.unlock();
 801             }
 802         } finally {
 803             readLock.unlock();
 804         }
 805         return this;
 806     }
 807 
 808     private SocketAddress unixBind(SocketAddress local) throws IOException {
 809         if (local == null) {
 810             return UnixDomainSockets.unnamed();
 811         } else {
 812             Path path = UnixDomainSockets.checkAddress(local).getPath();
 813             if (path.toString().isEmpty()) {
 814                 return UnixDomainSockets.unnamed();
 815             } else {
 816                 // bind to non-empty path
 817                 UnixDomainSockets.bind(fd, path);
 818                 return UnixDomainSockets.localAddress(fd);
 819             }
 820         }
 821     }
 822 
 823     private SocketAddress netBind(SocketAddress local) throws IOException {
 824         InetSocketAddress isa;
 825         if (local == null) {
 826             isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
 827         } else {
 828             isa = Net.checkAddress(local, family);
 829         }
 830         Net.bind(family, fd, isa.getAddress(), isa.getPort());
 831         return Net.localAddress(fd);
 832     }
 833 
 834     @Override
 835     public boolean isConnected() {
 836         return (state == ST_CONNECTED);
 837     }
 838 
 839     @Override
 840     public boolean isConnectionPending() {
 841         return (state == ST_CONNECTIONPENDING);
 842     }
 843 
 844     /**
 845      * Marks the beginning of a connect operation that might block.
 846      * @param blocking true if configured blocking
 847      * @param sa the remote socket address
 848      * @throws ClosedChannelException if the channel is closed
 849      * @throws AlreadyConnectedException if already connected
 850      * @throws ConnectionPendingException is a connection is pending
 851      * @throws IOException if the pre-connect hook fails
 852      */
 853     private void beginConnect(boolean blocking, SocketAddress sa)
 854         throws IOException
 855     {
 856         if (blocking) {
 857             // set hook for Thread.interrupt
 858             begin();
 859         }
 860         synchronized (stateLock) {
 861             ensureOpen();
 862             int state = this.state;
 863             if (state == ST_CONNECTED)
 864                 throw new AlreadyConnectedException();
 865             if (state == ST_CONNECTIONPENDING)
 866                 throw new ConnectionPendingException();
 867             assert state == ST_UNCONNECTED;
 868             this.state = ST_CONNECTIONPENDING;
 869 
 870             if (isNetSocket() && (localAddress == null)) {
 871                 InetSocketAddress isa = (InetSocketAddress) sa;
 872             }
 873             remoteAddress = sa;
 874 
 875             if (blocking) {
 876                 // record thread so it can be signalled if needed
 877                 readerThread = NativeThread.current();
 878             }
 879         }
 880     }
 881 
 882     /**
 883      * Marks the end of a connect operation that may have blocked.
 884      *
 885      * @throws AsynchronousCloseException if the channel was closed due to this
 886      * thread being interrupted on a blocking connect operation.
 887      * @throws IOException if completed and unable to obtain the local address
 888      */
 889     private void endConnect(boolean blocking, boolean completed)
 890         throws IOException
 891     {
 892         endRead(blocking, completed);
 893 
 894         if (completed) {
 895             synchronized (stateLock) {
 896                 if (state == ST_CONNECTIONPENDING) {
 897                     if (isUnixSocket()) {
 898                         localAddress = UnixDomainSockets.localAddress(fd);
 899                     } else {
 900                         localAddress = Net.localAddress(fd);
 901                     }
 902                     state = ST_CONNECTED;
 903                 }
 904             }
 905         }
 906     }
 907 
 908     /**
 909      * Checks the remote address to which this channel is to be connected.
 910      */
 911     private SocketAddress checkRemote(SocketAddress sa) {
 912         if (isUnixSocket()) {
 913             return UnixDomainSockets.checkAddress(sa);
 914         } else {
 915             InetSocketAddress isa = Net.checkAddress(sa, family);
 916             InetAddress address = isa.getAddress();
 917             if (address.isAnyLocalAddress()) {
 918                 int port = isa.getPort();
 919                 if (address instanceof Inet4Address) {
 920                     return new InetSocketAddress(Net.inet4LoopbackAddress(), port);
 921                 } else {
 922                     assert family == INET6;
 923                     return new InetSocketAddress(Net.inet6LoopbackAddress(), port);
 924                 }
 925             } else {
 926                 return isa;
 927             }
 928         }
 929     }
 930 
 931     @Override
 932     public boolean connect(SocketAddress remote) throws IOException {
 933         SocketAddress sa = checkRemote(remote);
 934         try {
 935             readLock.lock();
 936             try {
 937                 writeLock.lock();
 938                 try {
 939                     ensureOpen();
 940                     boolean blocking = isBlocking();
 941                     boolean connected = false;
 942                     try {
 943                         beginConnect(blocking, sa);
 944                         configureSocketNonBlockingIfVirtualThread();
 945                         int n;
 946                         if (isUnixSocket()) {
 947                             n = UnixDomainSockets.connect(fd, sa);
 948                         } else {
 949                             n = Net.connect(family, fd, sa);
 950                         }
 951                         if (n > 0) {
 952                             connected = true;
 953                         } else if (blocking) {
 954                             assert IOStatus.okayToRetry(n);
 955                             boolean polled = false;
 956                             while (!polled && isOpen()) {
 957                                 park(Net.POLLOUT);
 958                                 polled = Net.pollConnectNow(fd);
 959                             }
 960                             connected = polled && isOpen();
 961                         }
 962                     } finally {
 963                         endConnect(blocking, connected);
 964                     }
 965                     return connected;
 966                 } finally {
 967                     writeLock.unlock();
 968                 }
 969             } finally {
 970                 readLock.unlock();
 971             }
 972         } catch (IOException ioe) {
 973             // connect failed, close the channel
 974             close();
 975             throw Exceptions.ioException(ioe, sa);
 976         }
 977     }
 978 
 979     /**
 980      * Marks the beginning of a finishConnect operation that might block.
 981      *
 982      * @throws ClosedChannelException if the channel is closed
 983      * @throws NoConnectionPendingException if no connection is pending
 984      */
 985     private void beginFinishConnect(boolean blocking) throws ClosedChannelException {
 986         if (blocking) {
 987             // set hook for Thread.interrupt
 988             begin();
 989         }
 990         synchronized (stateLock) {
 991             ensureOpen();
 992             if (state != ST_CONNECTIONPENDING)
 993                 throw new NoConnectionPendingException();
 994             if (blocking) {
 995                 // record thread so it can be signalled if needed
 996                 readerThread = NativeThread.current();
 997             }
 998         }
 999     }
1000 
1001     /**
1002      * Marks the end of a finishConnect operation that may have blocked.
1003      *
1004      * @throws AsynchronousCloseException if the channel was closed due to this
1005      * thread being interrupted on a blocking connect operation.
1006      * @throws IOException if completed and unable to obtain the local address
1007      */
1008     private void endFinishConnect(boolean blocking, boolean completed)
1009         throws IOException
1010     {
1011         endRead(blocking, completed);
1012 
1013         if (completed) {
1014             synchronized (stateLock) {
1015                 if (state == ST_CONNECTIONPENDING) {
1016                     if (isUnixSocket()) {
1017                         localAddress = UnixDomainSockets.localAddress(fd);
1018                     } else {
1019                         localAddress = Net.localAddress(fd);
1020                     }
1021                     state = ST_CONNECTED;
1022                 }
1023             }
1024         }
1025     }
1026 
1027     @Override
1028     public boolean finishConnect() throws IOException {
1029         try {
1030             readLock.lock();
1031             try {
1032                 writeLock.lock();
1033                 try {
1034                     // no-op if already connected
1035                     if (isConnected())
1036                         return true;
1037 
1038                     ensureOpen();
1039                     boolean blocking = isBlocking();
1040                     boolean connected = false;
1041                     try {
1042                         beginFinishConnect(blocking);
1043                         boolean polled = Net.pollConnectNow(fd);
1044                         if (blocking) {
1045                             while (!polled && isOpen()) {
1046                                 park(Net.POLLOUT);
1047                                 polled = Net.pollConnectNow(fd);
1048                             }
1049                         }
1050                         connected = polled && isOpen();
1051                     } finally {
1052                         endFinishConnect(blocking, connected);
1053                     }
1054                     assert (blocking && connected) ^ !blocking;
1055                     return connected;
1056                 } finally {
1057                     writeLock.unlock();
1058                 }
1059             } finally {
1060                 readLock.unlock();
1061             }
1062         } catch (IOException ioe) {
1063             // connect failed, close the channel
1064             close();
1065             throw Exceptions.ioException(ioe, remoteAddress);
1066         }
1067     }
1068 
1069     /**
1070      * Closes the socket if there are no I/O operations in progress (or no I/O
1071      * operations tracked), and the channel is not registered with a Selector.
1072      */
1073     private boolean tryClose() throws IOException {
1074         assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
1075         if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
1076             state = ST_CLOSED;
1077             nd.close(fd);
1078             return true;
1079         } else {
1080             return false;
1081         }
1082     }
1083 
1084     /**
1085      * Invokes tryClose to attempt to close the socket.
1086      *
1087      * This method is used for deferred closing by I/O and Selector operations.
1088      */
1089     private void tryFinishClose() {
1090         try {
1091             tryClose();
1092         } catch (IOException ignore) { }
1093     }
1094 
1095     /**
1096      * Closes this channel when configured in blocking mode. If there are no I/O
1097      * operations in progress (or tracked), then the channel's socket is closed. If
1098      * there are I/O operations in progress then the behavior is platform specific.
1099      *
1100      * On Unix systems, the channel's socket is pre-closed. This unparks any virtual
1101      * threads that are blocked in I/O operations on this channel. If there are
1102      * platform threads blocked on the channel's socket then the socket is dup'ed
1103      * and the platform threads signalled. The final close is deferred until all I/O
1104      * operations complete.
1105      *
1106      * On Windows, the channel's socket is pre-closed. This unparks any virtual
1107      * threads that are blocked in I/O operations on this channel. If there are no
1108      * virtual threads blocked in I/O operations on this channel then the channel's
1109      * socket is closed. If there are virtual threads in I/O then the final close is
1110      * deferred until all I/O operations on virtual threads complete.
1111      *
1112      * Note that a channel configured blocking may be registered with a Selector
1113      * This arises when a key is canceled and the channel configured to blocking
1114      * mode before the key is flushed from the Selector.
1115      */
1116     private void implCloseBlockingMode() throws IOException {
1117         synchronized (stateLock) {
1118             assert state < ST_CLOSING;
1119             boolean connected = (state == ST_CONNECTED);
1120             state = ST_CLOSING;
1121 
1122             if (connected && Net.shouldShutdownWriteBeforeClose()) {
1123                 // shutdown output when linger interval not set to 0
1124                 try {
1125                     var SO_LINGER = StandardSocketOptions.SO_LINGER;
1126                     if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
1127                         Net.shutdown(fd, Net.SHUT_WR);
1128                     }
1129                 } catch (IOException ignore) { }
1130             }
1131 
1132             if (!tryClose()) {
1133                 // prepare file descriptor for closing
1134                 nd.preClose(fd, readerThread, writerThread);
1135             }
1136         }
1137     }
1138 
1139     /**
1140      * Closes this channel when configured in non-blocking mode.
1141      *
1142      * If the channel is registered with a Selector then the close is deferred
1143      * until the channel is flushed from all Selectors.
1144      *
1145      * If the socket is connected and the channel is registered with a Selector
1146      * then the socket is shutdown for writing so that the peer reads EOF. In
1147      * addition, if SO_LINGER is set to a non-zero value then it is disabled so
1148      * that the deferred close does not wait.
1149      */
1150     private void implCloseNonBlockingMode() throws IOException {
1151         boolean connected;
1152         synchronized (stateLock) {
1153             assert state < ST_CLOSING;
1154             connected = (state == ST_CONNECTED);
1155             state = ST_CLOSING;
1156         }
1157 
1158         // wait for any read/write operations to complete
1159         readLock.lock();
1160         readLock.unlock();
1161         writeLock.lock();
1162         writeLock.unlock();
1163 
1164         // if the socket cannot be closed because it's registered with a Selector
1165         // then shutdown the socket for writing.
1166         synchronized (stateLock) {
1167             if (state == ST_CLOSING && !tryClose() && connected && isRegistered()) {
1168                 try {
1169                     SocketOption<Integer> opt = StandardSocketOptions.SO_LINGER;
1170                     int interval = (int) Net.getSocketOption(fd, Net.UNSPEC, opt);
1171                     if (interval != 0) {
1172                         if (interval > 0) {
1173                             // disable SO_LINGER
1174                             Net.setSocketOption(fd, Net.UNSPEC, opt, -1);
1175                         }
1176                         Net.shutdown(fd, Net.SHUT_WR);
1177                     }
1178                 } catch (IOException ignore) { }
1179             }
1180         }
1181     }
1182 
1183     /**
1184      * Invoked by implCloseChannel to close the channel.
1185      */
1186     @Override
1187     protected void implCloseSelectableChannel() throws IOException {
1188         assert !isOpen();
1189         if (isBlocking()) {
1190             implCloseBlockingMode();
1191         } else {
1192             implCloseNonBlockingMode();
1193         }
1194     }
1195 
1196     @Override
1197     public void kill() {
1198         // wait for any read/write operations to complete before trying to close
1199         readLock.lock();
1200         readLock.unlock();
1201         writeLock.lock();
1202         writeLock.unlock();
1203         synchronized (stateLock) {
1204             if (state == ST_CLOSING) {
1205                 tryFinishClose();
1206             }
1207         }
1208     }
1209 
1210     @Override
1211     public SocketChannel shutdownInput() throws IOException {
1212         synchronized (stateLock) {
1213             ensureOpen();
1214             if (!isConnected())
1215                 throw new NotYetConnectedException();
1216             if (!isInputClosed) {
1217                 Net.shutdown(fd, Net.SHUT_RD);
1218                 long reader = readerThread;
1219                 if (NativeThread.isVirtualThread(reader)) {
1220                     Poller.stopPoll(fdVal, Net.POLLIN);
1221                 } else if (NativeThread.isNativeThread(reader)) {
1222                     NativeThread.signal(reader);
1223                 }
1224                 isInputClosed = true;
1225             }
1226             return this;
1227         }
1228     }
1229 
1230     @Override
1231     public SocketChannel shutdownOutput() throws IOException {
1232         synchronized (stateLock) {
1233             ensureOpen();
1234             if (!isConnected())
1235                 throw new NotYetConnectedException();
1236             if (!isOutputClosed) {
1237                 Net.shutdown(fd, Net.SHUT_WR);
1238                 long writer = writerThread;
1239                 if (NativeThread.isVirtualThread(writer)) {
1240                     Poller.stopPoll(fdVal, Net.POLLOUT);
1241                 } else if (NativeThread.isNativeThread(writer)) {
1242                     NativeThread.signal(writer);
1243                 }
1244                 isOutputClosed = true;
1245             }
1246             return this;
1247         }
1248     }
1249 
1250     boolean isInputOpen() {
1251         return !isInputClosed;
1252     }
1253 
1254     boolean isOutputOpen() {
1255         return !isOutputClosed;
1256     }
1257 
1258     /**
1259      * Waits for a connection attempt to finish with a timeout
1260      * @throws SocketTimeoutException if the connect timeout elapses
1261      */
1262     private boolean finishTimedConnect(long nanos) throws IOException {
1263         long startNanos = System.nanoTime();
1264         boolean polled = Net.pollConnectNow(fd);
1265         while (!polled && isOpen()) {
1266             long remainingNanos = nanos - (System.nanoTime() - startNanos);
1267             if (remainingNanos <= 0) {
1268                 throw new SocketTimeoutException("Connect timed out");
1269             }
1270             park(Net.POLLOUT, remainingNanos);
1271             polled = Net.pollConnectNow(fd);
1272         }
1273         return polled && isOpen();
1274     }
1275 
1276     /**
1277      * Attempts to establish a connection to the given socket address with a
1278      * timeout. Closes the socket if connection cannot be established.
1279      *
1280      * @apiNote This method is for use by the socket adaptor.
1281      *
1282      * @throws IllegalBlockingModeException if the channel is non-blocking
1283      * @throws SocketTimeoutException if the read timeout elapses
1284      */
1285     void blockingConnect(SocketAddress remote, long nanos) throws IOException {
1286         SocketAddress sa = checkRemote(remote);
1287         try {
1288             readLock.lock();
1289             try {
1290                 writeLock.lock();
1291                 try {
1292                     if (!isBlocking())
1293                         throw new IllegalBlockingModeException();
1294                     boolean connected = false;
1295                     try {
1296                         beginConnect(true, sa);
1297                         // change socket to non-blocking
1298                         lockedConfigureBlocking(false);
1299                         try {
1300                             int n;
1301                             if (isUnixSocket()) {
1302                                 n = UnixDomainSockets.connect(fd, sa);
1303                             } else {
1304                                 n = Net.connect(family, fd, sa);
1305                             }
1306                             connected = (n > 0) ? true : finishTimedConnect(nanos);
1307                         } finally {
1308                             // restore socket to blocking mode (if channel is open)
1309                             tryLockedConfigureBlocking(true);
1310                         }
1311                     } finally {
1312                         endConnect(true, connected);
1313                     }
1314                 } finally {
1315                     writeLock.unlock();
1316                 }
1317             } finally {
1318                 readLock.unlock();
1319             }
1320         } catch (IOException ioe) {
1321             // connect failed, close the channel
1322             close();
1323             throw Exceptions.ioException(ioe, sa);
1324         }
1325     }
1326 
1327     /**
1328      * Attempts to read bytes from the socket into the given byte array.
1329      */
1330     private int tryRead(byte[] b, int off, int len) throws IOException {
1331         ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
1332         assert dst.position() == 0;
1333         try {
1334             int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
1335             if (n > 0) {
1336                 dst.get(b, off, n);
1337             }
1338             return n;
1339         } finally{
1340             Util.offerFirstTemporaryDirectBuffer(dst);
1341         }
1342     }
1343 
1344     /**
1345      * Reads bytes from the socket into the given byte array with a timeout.
1346      * @throws SocketTimeoutException if the read timeout elapses
1347      */
1348     private int timedRead(byte[] b, int off, int len, long nanos) throws IOException {
1349         long startNanos = System.nanoTime();
1350         int n = tryRead(b, off, len);
1351         while (n == IOStatus.UNAVAILABLE && isOpen()) {
1352             long remainingNanos = nanos - (System.nanoTime() - startNanos);
1353             if (remainingNanos <= 0) {
1354                 throw new SocketTimeoutException("Read timed out");
1355             }
1356             park(Net.POLLIN, remainingNanos);
1357             n = tryRead(b, off, len);
1358         }
1359         return n;
1360     }
1361 
1362     /**
1363      * Reads bytes from the socket into the given byte array.
1364      *
1365      * @apiNote This method is for use by the socket adaptor.
1366      *
1367      * @throws IllegalBlockingModeException if the channel is non-blocking
1368      * @throws SocketTimeoutException if the read timeout elapses
1369      */
1370     int blockingRead(byte[] b, int off, int len, long nanos) throws IOException {
1371         Objects.checkFromIndexSize(off, len, b.length);
1372         if (len == 0) {
1373             // nothing to do
1374             return 0;
1375         }
1376 
1377         readLock.lock();
1378         try {
1379             ensureOpenAndConnected();
1380 
1381             // check that channel is configured blocking
1382             if (!isBlocking())
1383                 throw new IllegalBlockingModeException();
1384 
1385             int n = 0;
1386             try {
1387                 beginRead(true);
1388 
1389                 // check if connection has been reset
1390                 if (connectionReset)
1391                     throwConnectionReset();
1392 
1393                 // check if input is shutdown
1394                 if (isInputClosed)
1395                     return IOStatus.EOF;
1396 
1397                 if (nanos > 0) {
1398                     // change socket to non-blocking
1399                     lockedConfigureBlocking(false);
1400                     try {
1401                         n = timedRead(b, off, len, nanos);
1402                     } finally {
1403                         // restore socket to blocking mode (if channel is open)
1404                         tryLockedConfigureBlocking(true);
1405                     }
1406                 } else {
1407                     // read, no timeout
1408                     configureSocketNonBlockingIfVirtualThread();
1409                     n = tryRead(b, off, len);
1410                     while (IOStatus.okayToRetry(n) && isOpen()) {
1411                         park(Net.POLLIN);
1412                         n = tryRead(b, off, len);
1413                     }
1414                 }
1415             } catch (ConnectionResetException e) {
1416                 connectionReset = true;
1417                 throwConnectionReset();
1418             } finally {
1419                 endRead(true, n > 0);
1420                 if (n <= 0 && isInputClosed)
1421                     return IOStatus.EOF;
1422             }
1423             assert n > 0 || n == -1;
1424             return n;
1425         } finally {
1426             readLock.unlock();
1427         }
1428     }
1429 
1430     /**
1431      * Attempts to write a sequence of bytes to the socket from the given
1432      * byte array.
1433      */
1434     private int tryWrite(byte[] b, int off, int len) throws IOException {
1435         ByteBuffer src = Util.getTemporaryDirectBuffer(len);
1436         assert src.position() == 0;
1437         try {
1438             src.put(b, off, len);
1439             return nd.write(fd, ((DirectBuffer)src).address(), len);
1440         } finally {
1441             Util.offerFirstTemporaryDirectBuffer(src);
1442         }
1443     }
1444 
1445     /**
1446      * Writes a sequence of bytes to the socket from the given byte array.
1447      *
1448      * @apiNote This method is for use by the socket adaptor.
1449      */
1450     void blockingWriteFully(byte[] b, int off, int len) throws IOException {
1451         Objects.checkFromIndexSize(off, len, b.length);
1452         if (len == 0) {
1453             // nothing to do
1454             return;
1455         }
1456 
1457         writeLock.lock();
1458         try {
1459             ensureOpenAndConnected();
1460 
1461             // check that channel is configured blocking
1462             if (!isBlocking())
1463                 throw new IllegalBlockingModeException();
1464 
1465             // loop until all bytes have been written
1466             int pos = off;
1467             int end = off + len;
1468             try {
1469                 beginWrite(true);
1470                 configureSocketNonBlockingIfVirtualThread();
1471                 while (pos < end && isOpen()) {
1472                     int size = end - pos;
1473                     int n = tryWrite(b, pos, size);
1474                     while (IOStatus.okayToRetry(n) && isOpen()) {
1475                         park(Net.POLLOUT);
1476                         n = tryWrite(b, pos, size);
1477                     }
1478                     if (n > 0) {
1479                         pos += n;
1480                     }
1481                 }
1482             } finally {
1483                 endWrite(true, pos >= end);
1484             }
1485         } finally {
1486             writeLock.unlock();
1487         }
1488     }
1489 
1490     /**
1491      * Return the number of bytes in the socket input buffer.
1492      */
1493     int available() throws IOException {
1494         synchronized (stateLock) {
1495             ensureOpenAndConnected();
1496             if (isInputClosed) {
1497                 return 0;
1498             } else {
1499                 return Net.available(fd);
1500             }
1501         }
1502     }
1503 
1504     /**
1505      * Translates native poll revent ops into a ready operation ops
1506      */
1507     public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
1508         int intOps = ski.nioInterestOps();
1509         int oldOps = ski.nioReadyOps();
1510         int newOps = initialOps;
1511 
1512         if ((ops & Net.POLLNVAL) != 0) {
1513             // This should only happen if this channel is pre-closed while a
1514             // selection operation is in progress
1515             // ## Throw an error if this channel has not been pre-closed
1516             return false;
1517         }
1518 
1519         if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
1520             newOps = intOps;
1521             ski.nioReadyOps(newOps);
1522             return (newOps & ~oldOps) != 0;
1523         }
1524 
1525         boolean connected = isConnected();
1526         if (((ops & Net.POLLIN) != 0) &&
1527             ((intOps & SelectionKey.OP_READ) != 0) && connected)
1528             newOps |= SelectionKey.OP_READ;
1529 
1530         if (((ops & Net.POLLCONN) != 0) &&
1531             ((intOps & SelectionKey.OP_CONNECT) != 0) && isConnectionPending())
1532             newOps |= SelectionKey.OP_CONNECT;
1533 
1534         if (((ops & Net.POLLOUT) != 0) &&
1535             ((intOps & SelectionKey.OP_WRITE) != 0) && connected)
1536             newOps |= SelectionKey.OP_WRITE;
1537 
1538         ski.nioReadyOps(newOps);
1539         return (newOps & ~oldOps) != 0;
1540     }
1541 
1542     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
1543         return translateReadyOps(ops, ski.nioReadyOps(), ski);
1544     }
1545 
1546     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
1547         return translateReadyOps(ops, 0, ski);
1548     }
1549 
1550     /**
1551      * Translates an interest operation set into a native poll event set
1552      */
1553     public int translateInterestOps(int ops) {
1554         int newOps = 0;
1555         if ((ops & SelectionKey.OP_READ) != 0)
1556             newOps |= Net.POLLIN;
1557         if ((ops & SelectionKey.OP_WRITE) != 0)
1558             newOps |= Net.POLLOUT;
1559         if ((ops & SelectionKey.OP_CONNECT) != 0)
1560             newOps |= Net.POLLCONN;
1561         return newOps;
1562     }
1563 
1564     public FileDescriptor getFD() {
1565         return fd;
1566     }
1567 
1568     public int getFDVal() {
1569         return fdVal;
1570     }
1571 
1572     @Override
1573     public String toString() {
1574         StringBuilder sb = new StringBuilder();
1575         sb.append(this.getClass().getSuperclass().getName());
1576         sb.append('[');
1577         if (!isOpen())
1578             sb.append("closed");
1579         else {
1580             synchronized (stateLock) {
1581                 switch (state) {
1582                 case ST_UNCONNECTED:
1583                     sb.append("unconnected");
1584                     break;
1585                 case ST_CONNECTIONPENDING:
1586                     sb.append("connection-pending");
1587                     break;
1588                 case ST_CONNECTED:
1589                     sb.append("connected");
1590                     if (isInputClosed)
1591                         sb.append(" ishut");
1592                     if (isOutputClosed)
1593                         sb.append(" oshut");
1594                     break;
1595                 }
1596                 SocketAddress addr = localAddress();
1597                 if (addr != null) {
1598                     sb.append(" local=");
1599                     sb.append(addr);
1600                 }
1601                 if (remoteAddress() != null) {
1602                     sb.append(" remote=");
1603                     sb.append(remoteAddress());
1604                 }
1605             }
1606         }
1607         sb.append(']');
1608         return sb.toString();
1609     }
1610 }