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     // Threads doing reads and writes, for signalling
 117     private Thread readerThread;
 118     private Thread 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.threadToSignal();
 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 = null;
 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.threadToSignal();
 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 = null;
 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.threadToSignal();
 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 = null;
 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.threadToSignal();
 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.threadToSignal();
 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 == null) && (writerThread == null) && !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                 if (readerThread != null && readerThread.isVirtual()) {
1219                     Poller.stopPoll(readerThread);
1220                 }
1221                 isInputClosed = true;
1222             }
1223             return this;
1224         }
1225     }
1226 
1227     @Override
1228     public SocketChannel shutdownOutput() throws IOException {
1229         synchronized (stateLock) {
1230             ensureOpen();
1231             if (!isConnected())
1232                 throw new NotYetConnectedException();
1233             if (!isOutputClosed) {
1234                 Net.shutdown(fd, Net.SHUT_WR);
1235                 if (writerThread != null && writerThread.isVirtual()) {
1236                     Poller.stopPoll(writerThread);
1237                 }
1238                 isOutputClosed = true;
1239             }
1240             return this;
1241         }
1242     }
1243 
1244     boolean isInputOpen() {
1245         return !isInputClosed;
1246     }
1247 
1248     boolean isOutputOpen() {
1249         return !isOutputClosed;
1250     }
1251 
1252     /**
1253      * Waits for a connection attempt to finish with a timeout
1254      * @throws SocketTimeoutException if the connect timeout elapses
1255      */
1256     private boolean finishTimedConnect(long nanos) throws IOException {
1257         long startNanos = System.nanoTime();
1258         boolean polled = Net.pollConnectNow(fd);
1259         while (!polled && isOpen()) {
1260             long remainingNanos = nanos - (System.nanoTime() - startNanos);
1261             if (remainingNanos <= 0) {
1262                 throw new SocketTimeoutException("Connect timed out");
1263             }
1264             park(Net.POLLOUT, remainingNanos);
1265             polled = Net.pollConnectNow(fd);
1266         }
1267         return polled && isOpen();
1268     }
1269 
1270     /**
1271      * Attempts to establish a connection to the given socket address with a
1272      * timeout. Closes the socket if connection cannot be established.
1273      *
1274      * @apiNote This method is for use by the socket adaptor.
1275      *
1276      * @throws IllegalBlockingModeException if the channel is non-blocking
1277      * @throws SocketTimeoutException if the read timeout elapses
1278      */
1279     void blockingConnect(SocketAddress remote, long nanos) throws IOException {
1280         SocketAddress sa = checkRemote(remote);
1281         try {
1282             readLock.lock();
1283             try {
1284                 writeLock.lock();
1285                 try {
1286                     if (!isBlocking())
1287                         throw new IllegalBlockingModeException();
1288                     boolean connected = false;
1289                     try {
1290                         beginConnect(true, sa);
1291                         // change socket to non-blocking
1292                         lockedConfigureBlocking(false);
1293                         try {
1294                             int n;
1295                             if (isUnixSocket()) {
1296                                 n = UnixDomainSockets.connect(fd, sa);
1297                             } else {
1298                                 n = Net.connect(family, fd, sa);
1299                             }
1300                             connected = (n > 0) ? true : finishTimedConnect(nanos);
1301                         } finally {
1302                             // restore socket to blocking mode (if channel is open)
1303                             tryLockedConfigureBlocking(true);
1304                         }
1305                     } finally {
1306                         endConnect(true, connected);
1307                     }
1308                 } finally {
1309                     writeLock.unlock();
1310                 }
1311             } finally {
1312                 readLock.unlock();
1313             }
1314         } catch (IOException ioe) {
1315             // connect failed, close the channel
1316             close();
1317             throw Exceptions.ioException(ioe, sa);
1318         }
1319     }
1320 
1321     /**
1322      * Attempts to read bytes from the socket into the given byte array.
1323      */
1324     private int tryRead(byte[] b, int off, int len) throws IOException {
1325         ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
1326         assert dst.position() == 0;
1327         try {
1328             int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
1329             if (n > 0) {
1330                 dst.get(b, off, n);
1331             }
1332             return n;
1333         } finally{
1334             Util.offerFirstTemporaryDirectBuffer(dst);
1335         }
1336     }
1337 
1338     /**
1339      * Reads bytes from the socket into the given byte array with a timeout.
1340      * @throws SocketTimeoutException if the read timeout elapses
1341      */
1342     private int timedRead(byte[] b, int off, int len, long nanos) throws IOException {
1343         long startNanos = System.nanoTime();
1344         int n = tryRead(b, off, len);
1345         while (n == IOStatus.UNAVAILABLE && isOpen()) {
1346             long remainingNanos = nanos - (System.nanoTime() - startNanos);
1347             if (remainingNanos <= 0) {
1348                 throw new SocketTimeoutException("Read timed out");
1349             }
1350             park(Net.POLLIN, remainingNanos);
1351             n = tryRead(b, off, len);
1352         }
1353         return n;
1354     }
1355 
1356     /**
1357      * Reads bytes from the socket into the given byte array.
1358      *
1359      * @apiNote This method is for use by the socket adaptor.
1360      *
1361      * @throws IllegalBlockingModeException if the channel is non-blocking
1362      * @throws SocketTimeoutException if the read timeout elapses
1363      */
1364     int blockingRead(byte[] b, int off, int len, long nanos) throws IOException {
1365         Objects.checkFromIndexSize(off, len, b.length);
1366         if (len == 0) {
1367             // nothing to do
1368             return 0;
1369         }
1370 
1371         readLock.lock();
1372         try {
1373             ensureOpenAndConnected();
1374 
1375             // check that channel is configured blocking
1376             if (!isBlocking())
1377                 throw new IllegalBlockingModeException();
1378 
1379             int n = 0;
1380             try {
1381                 beginRead(true);
1382 
1383                 // check if connection has been reset
1384                 if (connectionReset)
1385                     throwConnectionReset();
1386 
1387                 // check if input is shutdown
1388                 if (isInputClosed)
1389                     return IOStatus.EOF;
1390 
1391                 if (nanos > 0) {
1392                     // change socket to non-blocking
1393                     lockedConfigureBlocking(false);
1394                     try {
1395                         n = timedRead(b, off, len, nanos);
1396                     } finally {
1397                         // restore socket to blocking mode (if channel is open)
1398                         tryLockedConfigureBlocking(true);
1399                     }
1400                 } else {
1401                     // read, no timeout
1402                     configureSocketNonBlockingIfVirtualThread();
1403                     n = tryRead(b, off, len);
1404                     while (IOStatus.okayToRetry(n) && isOpen()) {
1405                         park(Net.POLLIN);
1406                         n = tryRead(b, off, len);
1407                     }
1408                 }
1409             } catch (ConnectionResetException e) {
1410                 connectionReset = true;
1411                 throwConnectionReset();
1412             } finally {
1413                 endRead(true, n > 0);
1414                 if (n <= 0 && isInputClosed)
1415                     return IOStatus.EOF;
1416             }
1417             assert n > 0 || n == -1;
1418             return n;
1419         } finally {
1420             readLock.unlock();
1421         }
1422     }
1423 
1424     /**
1425      * Attempts to write a sequence of bytes to the socket from the given
1426      * byte array.
1427      */
1428     private int tryWrite(byte[] b, int off, int len) throws IOException {
1429         ByteBuffer src = Util.getTemporaryDirectBuffer(len);
1430         assert src.position() == 0;
1431         try {
1432             src.put(b, off, len);
1433             return nd.write(fd, ((DirectBuffer)src).address(), len);
1434         } finally {
1435             Util.offerFirstTemporaryDirectBuffer(src);
1436         }
1437     }
1438 
1439     /**
1440      * Writes a sequence of bytes to the socket from the given byte array.
1441      *
1442      * @apiNote This method is for use by the socket adaptor.
1443      */
1444     void blockingWriteFully(byte[] b, int off, int len) throws IOException {
1445         Objects.checkFromIndexSize(off, len, b.length);
1446         if (len == 0) {
1447             // nothing to do
1448             return;
1449         }
1450 
1451         writeLock.lock();
1452         try {
1453             ensureOpenAndConnected();
1454 
1455             // check that channel is configured blocking
1456             if (!isBlocking())
1457                 throw new IllegalBlockingModeException();
1458 
1459             // loop until all bytes have been written
1460             int pos = off;
1461             int end = off + len;
1462             try {
1463                 beginWrite(true);
1464                 configureSocketNonBlockingIfVirtualThread();
1465                 while (pos < end && isOpen()) {
1466                     int size = end - pos;
1467                     int n = tryWrite(b, pos, size);
1468                     while (IOStatus.okayToRetry(n) && isOpen()) {
1469                         park(Net.POLLOUT);
1470                         n = tryWrite(b, pos, size);
1471                     }
1472                     if (n > 0) {
1473                         pos += n;
1474                     }
1475                 }
1476             } finally {
1477                 endWrite(true, pos >= end);
1478             }
1479         } finally {
1480             writeLock.unlock();
1481         }
1482     }
1483 
1484     /**
1485      * Return the number of bytes in the socket input buffer.
1486      */
1487     int available() throws IOException {
1488         synchronized (stateLock) {
1489             ensureOpenAndConnected();
1490             if (isInputClosed) {
1491                 return 0;
1492             } else {
1493                 return Net.available(fd);
1494             }
1495         }
1496     }
1497 
1498     /**
1499      * Translates native poll revent ops into a ready operation ops
1500      */
1501     public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
1502         int intOps = ski.nioInterestOps();
1503         int oldOps = ski.nioReadyOps();
1504         int newOps = initialOps;
1505 
1506         if ((ops & Net.POLLNVAL) != 0) {
1507             // This should only happen if this channel is pre-closed while a
1508             // selection operation is in progress
1509             // ## Throw an error if this channel has not been pre-closed
1510             return false;
1511         }
1512 
1513         if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
1514             newOps = intOps;
1515             ski.nioReadyOps(newOps);
1516             return (newOps & ~oldOps) != 0;
1517         }
1518 
1519         boolean connected = isConnected();
1520         if (((ops & Net.POLLIN) != 0) &&
1521             ((intOps & SelectionKey.OP_READ) != 0) && connected)
1522             newOps |= SelectionKey.OP_READ;
1523 
1524         if (((ops & Net.POLLCONN) != 0) &&
1525             ((intOps & SelectionKey.OP_CONNECT) != 0) && isConnectionPending())
1526             newOps |= SelectionKey.OP_CONNECT;
1527 
1528         if (((ops & Net.POLLOUT) != 0) &&
1529             ((intOps & SelectionKey.OP_WRITE) != 0) && connected)
1530             newOps |= SelectionKey.OP_WRITE;
1531 
1532         ski.nioReadyOps(newOps);
1533         return (newOps & ~oldOps) != 0;
1534     }
1535 
1536     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
1537         return translateReadyOps(ops, ski.nioReadyOps(), ski);
1538     }
1539 
1540     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
1541         return translateReadyOps(ops, 0, ski);
1542     }
1543 
1544     /**
1545      * Translates an interest operation set into a native poll event set
1546      */
1547     public int translateInterestOps(int ops) {
1548         int newOps = 0;
1549         if ((ops & SelectionKey.OP_READ) != 0)
1550             newOps |= Net.POLLIN;
1551         if ((ops & SelectionKey.OP_WRITE) != 0)
1552             newOps |= Net.POLLOUT;
1553         if ((ops & SelectionKey.OP_CONNECT) != 0)
1554             newOps |= Net.POLLCONN;
1555         return newOps;
1556     }
1557 
1558     public FileDescriptor getFD() {
1559         return fd;
1560     }
1561 
1562     public int getFDVal() {
1563         return fdVal;
1564     }
1565 
1566     @Override
1567     public String toString() {
1568         StringBuilder sb = new StringBuilder();
1569         sb.append(this.getClass().getSuperclass().getName());
1570         sb.append('[');
1571         if (!isOpen())
1572             sb.append("closed");
1573         else {
1574             synchronized (stateLock) {
1575                 switch (state) {
1576                 case ST_UNCONNECTED:
1577                     sb.append("unconnected");
1578                     break;
1579                 case ST_CONNECTIONPENDING:
1580                     sb.append("connection-pending");
1581                     break;
1582                 case ST_CONNECTED:
1583                     sb.append("connected");
1584                     if (isInputClosed)
1585                         sb.append(" ishut");
1586                     if (isOutputClosed)
1587                         sb.append(" oshut");
1588                     break;
1589                 }
1590                 SocketAddress addr = localAddress();
1591                 if (addr != null) {
1592                     sb.append(" local=");
1593                     sb.append(addr);
1594                 }
1595                 if (remoteAddress() != null) {
1596                     sb.append(" remote=");
1597                     sb.append(remoteAddress());
1598                 }
1599             }
1600         }
1601         sb.append(']');
1602         return sb.toString();
1603     }
1604 }