View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.uring;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.buffer.ByteBufHolder;
21  import io.netty.buffer.ByteBufUtil;
22  import io.netty.buffer.Unpooled;
23  import io.netty.channel.AbstractChannel;
24  import io.netty.channel.Channel;
25  import io.netty.channel.ChannelConfig;
26  import io.netty.channel.ChannelFuture;
27  import io.netty.channel.ChannelFutureListener;
28  import io.netty.channel.ChannelOption;
29  import io.netty.channel.ChannelOutboundBuffer;
30  import io.netty.channel.ChannelPromise;
31  import io.netty.channel.ConnectTimeoutException;
32  import io.netty.channel.EventLoop;
33  import io.netty.channel.IoEvent;
34  import io.netty.channel.IoEventLoop;
35  import io.netty.channel.IoRegistration;
36  import io.netty.channel.RecvByteBufAllocator;
37  import io.netty.channel.ServerChannel;
38  import io.netty.channel.socket.ChannelInputShutdownEvent;
39  import io.netty.channel.socket.ChannelInputShutdownReadComplete;
40  import io.netty.channel.socket.SocketChannelConfig;
41  import io.netty.channel.unix.Buffer;
42  import io.netty.channel.unix.DomainSocketAddress;
43  import io.netty.channel.unix.Errors;
44  import io.netty.channel.unix.FileDescriptor;
45  import io.netty.channel.unix.IovArray;
46  import io.netty.channel.unix.UnixChannel;
47  import io.netty.channel.unix.UnixChannelUtil;
48  import io.netty.util.ReferenceCountUtil;
49  import io.netty.util.concurrent.PromiseNotifier;
50  import io.netty.util.internal.CleanableDirectBuffer;
51  import io.netty.util.internal.StringUtil;
52  import io.netty.util.internal.logging.InternalLogger;
53  import io.netty.util.internal.logging.InternalLoggerFactory;
54  
55  import java.io.IOException;
56  import java.net.InetSocketAddress;
57  import java.net.SocketAddress;
58  import java.nio.ByteBuffer;
59  import java.nio.channels.AlreadyConnectedException;
60  import java.nio.channels.ClosedChannelException;
61  import java.nio.channels.ConnectionPendingException;
62  import java.nio.channels.NotYetConnectedException;
63  import java.nio.channels.UnresolvedAddressException;
64  import java.util.concurrent.ScheduledFuture;
65  import java.util.concurrent.TimeUnit;
66  
67  import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE;
68  import static io.netty.channel.unix.Errors.ERROR_EALREADY_NEGATIVE;
69  import static io.netty.channel.unix.UnixChannelUtil.computeRemoteAddr;
70  import static io.netty.util.internal.ObjectUtil.checkNotNull;
71  import static io.netty.util.internal.StringUtil.className;
72  
73  
74  abstract class AbstractIoUringChannel extends AbstractChannel implements UnixChannel {
75      private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractIoUringChannel.class);
76      final LinuxSocket socket;
77      protected volatile boolean active;
78  
79      // Different masks for outstanding I/O operations.
80      private static final int POLL_IN_SCHEDULED = 1;
81      private static final int POLL_OUT_SCHEDULED = 1 << 2;
82      private static final int POLL_RDHUP_SCHEDULED = 1 << 3;
83      private static final int WRITE_SCHEDULED = 1 << 4;
84      private static final int READ_SCHEDULED = 1 << 5;
85      private static final int CONNECT_SCHEDULED = 1 << 6;
86  
87      private short opsId = Short.MIN_VALUE;
88  
89      private long pollInId;
90      private long pollOutId;
91      private long pollRdhupId;
92      private long connectId;
93  
94      // A byte is enough for now.
95      private byte ioState;
96  
97      // It's possible that multiple read / writes are issued. We need to keep track of these.
98      // Let's limit the amount of pending writes and reads by Short.MAX_VALUE. Maybe Byte.MAX_VALUE would also be good
99      // enough but let's be a bit more flexible for now.
100     private short numOutstandingWrites;
101     // A value of -1 means that multi-shot is used and so reads will be issued as long as the request is not canceled.
102     private short numOutstandingReads;
103 
104     private boolean readPending;
105     private boolean inReadComplete;
106     private boolean socketHasMoreData;
107 
108     private static final class DelayedClose {
109         private final ChannelPromise promise;
110         private final Throwable cause;
111         private final ClosedChannelException closeCause;
112 
113         DelayedClose(ChannelPromise promise, Throwable cause, ClosedChannelException closeCause) {
114             this.promise = promise;
115             this.cause = cause;
116             this.closeCause = closeCause;
117         }
118     }
119     private DelayedClose delayedClose;
120     private boolean inputClosedSeenErrorOnRead;
121 
122     /**
123      * The future of the current connection attempt.  If not null, subsequent connection attempts will fail.
124      */
125     private ChannelPromise connectPromise;
126     private ScheduledFuture<?> connectTimeoutFuture;
127     private SocketAddress requestedRemoteAddress;
128     private CleanableDirectBuffer cleanable;
129     private ByteBuffer remoteAddressMemory;
130     private MsgHdrMemoryArray msgHdrMemoryArray;
131 
132     private IoRegistration registration;
133 
134     private volatile SocketAddress local;
135     private volatile SocketAddress remote;
136 
137     AbstractIoUringChannel(final Channel parent, LinuxSocket socket, boolean active) {
138         super(parent);
139         this.socket = checkNotNull(socket, "fd");
140 
141         if (active) {
142             // Directly cache the remote and local addresses
143             // See https://github.com/netty/netty/issues/2359
144             this.active = true;
145             this.local = socket.localAddress();
146             this.remote = socket.remoteAddress();
147         }
148 
149         logger.trace("Create {} Socket: {}", this instanceof ServerChannel ? "Server" : "Channel", socket.intValue());
150     }
151 
152     AbstractIoUringChannel(Channel parent, LinuxSocket fd, SocketAddress remote) {
153         super(parent);
154         this.socket = checkNotNull(fd, "fd");
155         this.active = true;
156 
157         // Directly cache the remote and local addresses
158         // See https://github.com/netty/netty/issues/2359
159         this.remote = remote;
160         this.local = fd.localAddress();
161     }
162 
163     // Called once a Channel changed from AUTO_READ=true to AUTO_READ=false
164     final void autoReadCleared() {
165         if (!isRegistered()) {
166             return;
167         }
168         IoRegistration registration = this.registration;
169         if (registration == null || !registration.isValid()) {
170             return;
171         }
172         if (eventLoop().inEventLoop()) {
173             clearRead();
174         } else {
175             eventLoop().execute(this::clearRead);
176         }
177     }
178 
179     private void clearRead() {
180         assert eventLoop().inEventLoop();
181         readPending = false;
182         IoRegistration registration = this.registration;
183         if (registration == null || !registration.isValid()) {
184             return;
185         }
186         // Also cancel all outstanding reads as the user did signal there is no more desire to read.
187         cancelOutstandingReads(registration(), numOutstandingReads);
188     }
189 
190     /**
191      * Returns the next id that should be used when submitting {@link IoUringIoOps}.
192      *
193      * @return  opsId
194      */
195     protected final short nextOpsId() {
196         short id = opsId++;
197 
198         // We use 0 for "none".
199         if (id == 0) {
200             id = opsId++;
201         }
202         return id;
203     }
204 
205     public final boolean isOpen() {
206         return socket.isOpen();
207     }
208 
209     @Override
210     public boolean isActive() {
211         return active;
212     }
213 
214     @Override
215     public final FileDescriptor fd() {
216         return socket;
217     }
218 
219     private AbstractUringUnsafe ioUringUnsafe() {
220         return (AbstractUringUnsafe) unsafe();
221     }
222 
223     @Override
224     protected boolean isCompatible(final EventLoop loop) {
225         return loop instanceof IoEventLoop && ((IoEventLoop) loop).isCompatible(AbstractUringUnsafe.class);
226     }
227 
228     protected final ByteBuf newDirectBuffer(ByteBuf buf) {
229         return newDirectBuffer(buf, buf);
230     }
231 
232     protected boolean allowMultiShotPollIn() {
233         return IoUring.isPollAddMultishotEnabled();
234     }
235 
236     protected final ByteBuf newDirectBuffer(Object holder, ByteBuf buf) {
237         final int readableBytes = buf.readableBytes();
238         if (readableBytes == 0) {
239             ReferenceCountUtil.release(holder);
240             return Unpooled.EMPTY_BUFFER;
241         }
242 
243         final ByteBufAllocator alloc = alloc();
244         if (alloc.isDirectBufferPooled()) {
245             return newDirectBuffer0(holder, buf, alloc, readableBytes);
246         }
247 
248         final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
249         if (directBuf == null) {
250             return newDirectBuffer0(holder, buf, alloc, readableBytes);
251         }
252 
253         directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
254         ReferenceCountUtil.safeRelease(holder);
255         return directBuf;
256     }
257 
258     private static ByteBuf newDirectBuffer0(Object holder, ByteBuf buf, ByteBufAllocator alloc, int capacity) {
259         final ByteBuf directBuf = alloc.directBuffer(capacity);
260         directBuf.writeBytes(buf, buf.readerIndex(), capacity);
261         ReferenceCountUtil.safeRelease(holder);
262         return directBuf;
263     }
264 
265     /**
266      * Cancel all outstanding reads
267      *
268      * @param registration          the {@link IoRegistration}.
269      * @param numOutstandingReads   the number of outstanding reads, or {@code -1} if multi-shot was used.
270      */
271     protected abstract void cancelOutstandingReads(IoRegistration registration, int numOutstandingReads);
272 
273     /**
274      * Cancel all outstanding writes
275      *
276      * @param registration          the {@link IoRegistration}.
277      * @param numOutstandingWrites  the number of outstanding writes.
278      */
279     protected abstract void cancelOutstandingWrites(IoRegistration registration, int numOutstandingWrites);
280 
281     @Override
282     protected void doDisconnect() throws Exception {
283     }
284 
285     private void freeRemoteAddressMemory() {
286         if (remoteAddressMemory != null) {
287             cleanable.clean();
288             cleanable = null;
289             remoteAddressMemory = null;
290         }
291     }
292 
293     private void freeMsgHdrArray() {
294         if (msgHdrMemoryArray != null) {
295             msgHdrMemoryArray.release();
296             msgHdrMemoryArray = null;
297         }
298     }
299 
300     @Override
301     protected void doClose() throws Exception {
302         active = false;
303 
304         if (registration != null) {
305             if (socket.markClosed()) {
306                 int fd = fd().intValue();
307                 IoUringIoOps ops = IoUringIoOps.newClose(fd, (byte) 0, nextOpsId());
308                 registration.submit(ops);
309             }
310         } else {
311             // This one was never registered just use a syscall to close.
312             socket.close();
313             ioUringUnsafe().unregistered();
314         }
315     }
316 
317     @Override
318     protected final void doBeginRead() {
319         if (inputClosedSeenErrorOnRead) {
320             // We did see an error while reading and so closed the input. Stop reading.
321             return;
322         }
323         if (readPending) {
324             // We already have a read pending.
325             return;
326         }
327         readPending = true;
328         if (inReadComplete || !isActive()) {
329             // We are currently in the readComplete(...) callback which might issue more reads by itself.
330             // If readComplete(...) will not issue more reads itself it will pick up the readPending flag, reset it and
331             // call doBeginReadNow().
332             return;
333         }
334         doBeginReadNow();
335     }
336 
337     private void doBeginReadNow() {
338         if (inputClosedSeenErrorOnRead) {
339             // We did see an error while reading and so closed the input.
340             return;
341         }
342         if (!isPollInFirst() ||
343                 // If the socket was not empty, and we stopped reading we need to ensure we just force the
344                 // read as POLLIN might be edge-triggered (in case of POLL_ADD_MULTI).
345                 socketHasMoreData) {
346             // If the socket is blocking we will directly call scheduleFirstReadIfNeeded() as we can use FASTPOLL.
347             ioUringUnsafe().scheduleFirstReadIfNeeded();
348         } else if ((ioState & POLL_IN_SCHEDULED) == 0) {
349             ioUringUnsafe().schedulePollIn();
350         }
351     }
352 
353     @Override
354     protected void doWrite(ChannelOutboundBuffer in) {
355         scheduleWriteIfNeeded(in, true);
356     }
357 
358     protected void scheduleWriteIfNeeded(ChannelOutboundBuffer in, boolean submitAndRunNow) {
359         if ((ioState & WRITE_SCHEDULED) != 0) {
360             return;
361         }
362         if (scheduleWrite(in) > 0) {
363             ioState |= WRITE_SCHEDULED;
364             if (submitAndRunNow && !isWritable()) {
365                 submitAndRunNow();
366             }
367         }
368     }
369 
370     protected void submitAndRunNow() {
371         // NOOP
372     }
373 
374     private int scheduleWrite(ChannelOutboundBuffer in) {
375         if (delayedClose != null || numOutstandingWrites == Short.MAX_VALUE) {
376             return 0;
377         }
378         if (in == null) {
379             return 0;
380         }
381 
382         int msgCount = in.size();
383         if (msgCount == 0) {
384             return 0;
385         }
386         Object msg = in.current();
387 
388         if (msgCount > 1 && in.current() instanceof ByteBuf) {
389             numOutstandingWrites = (short) ioUringUnsafe().scheduleWriteMultiple(in);
390         } else if (msg instanceof ByteBuf && ((ByteBuf) msg).nioBufferCount() > 1 ||
391                     (msg instanceof ByteBufHolder && ((ByteBufHolder) msg).content().nioBufferCount() > 1)) {
392             // We also need some special handling for CompositeByteBuf
393             numOutstandingWrites = (short) ioUringUnsafe().scheduleWriteMultiple(in);
394         } else {
395             numOutstandingWrites = (short) ioUringUnsafe().scheduleWriteSingle(msg);
396         }
397         // Ensure we never overflow
398         assert numOutstandingWrites > 0;
399         return numOutstandingWrites;
400     }
401 
402     protected final IoRegistration registration() {
403         assert registration != null;
404         return registration;
405     }
406 
407     private void schedulePollOut() {
408         pollOutId = schedulePollAdd(POLL_OUT_SCHEDULED, Native.POLLOUT, false);
409     }
410 
411     final void schedulePollRdHup() {
412         pollRdhupId = schedulePollAdd(POLL_RDHUP_SCHEDULED, Native.POLLRDHUP, false);
413     }
414 
415     protected abstract boolean isStreamSocket();
416 
417     private long schedulePollAdd(int ioMask, int mask, boolean multishot) {
418         assert (ioState & ioMask) == 0;
419         int fd = fd().intValue();
420         IoRegistration registration = registration();
421         IoUringIoOps ops = IoUringIoOps.newPollAdd(
422                 fd, (byte) 0, mask, multishot ? Native.IORING_POLL_ADD_MULTI : 0, nextOpsId());
423         long id = registration.submit(ops);
424         if (id != 0) {
425             ioState |= (byte) ioMask;
426         }
427         return id;
428     }
429 
430     final void resetCachedAddresses() {
431         local = socket.localAddress();
432         remote = socket.remoteAddress();
433     }
434 
435     protected abstract class AbstractUringUnsafe extends AbstractUnsafe implements IoUringIoHandle {
436         private IoUringRecvByteAllocatorHandle allocHandle;
437         private boolean closed;
438         private boolean socketIsEmpty;
439         private ChannelPromise deregisterPromise;
440 
441         /**
442          * Schedule the write of multiple messages in the {@link ChannelOutboundBuffer} and returns the number of
443          * {@link #writeComplete(byte, int, int, short)} calls that are expected because of the scheduled write.
444          */
445         protected abstract int scheduleWriteMultiple(ChannelOutboundBuffer in);
446 
447         /**
448          * Schedule the write of a single message and returns the number of
449          * {@link #writeComplete(byte, int, int, short)} calls that are expected because of the scheduled write.
450          */
451         protected abstract int scheduleWriteSingle(Object msg);
452 
453         @Override
454         public final void handle(IoRegistration registration, IoEvent ioEvent) {
455             IoUringIoEvent event = (IoUringIoEvent) ioEvent;
456             byte op = event.opcode();
457             int res = event.res();
458             int flags = event.flags();
459             short data = (short) event.userData();
460             switch (op) {
461                 case Native.IORING_OP_RECV:
462                 case Native.IORING_OP_ACCEPT:
463                 case Native.IORING_OP_RECVMSG:
464                 case Native.IORING_OP_READ:
465                     readComplete(op, res, flags, data);
466                     break;
467                 case Native.IORING_OP_WRITEV:
468                 case Native.IORING_OP_SEND:
469                 case Native.IORING_OP_SENDMSG:
470                 case Native.IORING_OP_WRITE:
471                 case Native.IORING_OP_SPLICE:
472                 case Native.IORING_OP_SEND_ZC:
473                 case Native.IORING_OP_SENDMSG_ZC:
474                     writeComplete(op, res, flags, data);
475                     break;
476                 case Native.IORING_OP_POLL_ADD:
477                     pollAddComplete(res, flags, data);
478                     break;
479                 case Native.IORING_OP_ASYNC_CANCEL:
480                     cancelComplete0(op, res, flags, data);
481                     break;
482                 case Native.IORING_OP_CONNECT:
483                     connectComplete(op, res, flags, data);
484 
485                     // once the connect was completed we can also free some resources that are not needed anymore.
486                     freeMsgHdrArray();
487                     freeRemoteAddressMemory();
488                     break;
489                 case Native.IORING_OP_CLOSE:
490                     if (res != Native.ERRNO_ECANCELED_NEGATIVE) {
491                         if (delayedClose != null) {
492                             delayedClose.promise.setSuccess();
493                         }
494                         closed = true;
495                     }
496                     break;
497                 default:
498                     break;
499             }
500 
501             // We delay the actual close if there is still a write or read scheduled, let's see if there
502             // was a close that needs to be done now.
503             handleDelayedClosed();
504 
505             if (ioState == 0 && (closed || !isRegistered())) {
506                 // Cancel the registration now.
507                 registration.cancel();
508             }
509         }
510 
511         @Override
512         public void unregistered() {
513             freeMsgHdrArray();
514             freeRemoteAddressMemory();
515 
516             // Check if we need to notify about the deregistration.
517             if (deregisterPromise != null) {
518                 ChannelPromise promise = deregisterPromise;
519                 deregisterPromise = null;
520                 promise.setSuccess();
521             }
522         }
523 
524         private void handleDelayedClosed() {
525             if (delayedClose != null && canCloseNow()) {
526                 closeNow();
527             }
528         }
529 
530         private void pollAddComplete(int res, int flags, short data) {
531             if ((res & Native.POLLOUT) != 0) {
532                 pollOut(res);
533             }
534             if ((res & Native.POLLIN) != 0) {
535                 pollIn(res, flags, data);
536             }
537             if ((res & Native.POLLRDHUP) != 0) {
538                 pollRdHup(res);
539             }
540         }
541 
542         @Override
543         public final void close() throws Exception {
544             close(voidPromise());
545         }
546 
547         @Override
548         protected void close(ChannelPromise promise, Throwable cause, ClosedChannelException closeCause) {
549             if (closeFuture().isDone()) {
550                 // Closed already before.
551                 safeSetSuccess(promise);
552                 return;
553             }
554             if (delayedClose == null) {
555                 // We have a write operation pending that should be completed asap.
556                 // We will do the actual close operation one this write result is returned as otherwise
557                 // we may get into trouble as we may close the fd while we did not process the write yet.
558                 delayedClose = new DelayedClose(promise.isVoid() ? newPromise() : promise, cause, closeCause);
559             } else {
560                 delayedClose.promise.addListener(new PromiseNotifier<>(false, promise));
561                 return;
562             }
563 
564             boolean cancelConnect = false;
565             try {
566                 ChannelPromise connectPromise = AbstractIoUringChannel.this.connectPromise;
567                 if (connectPromise != null) {
568                     // Use tryFailure() instead of setFailure() to avoid the race against cancel().
569                     connectPromise.tryFailure(new ClosedChannelException());
570                     AbstractIoUringChannel.this.connectPromise = null;
571                     cancelConnect = true;
572                 }
573 
574                 cancelConnectTimeoutFuture();
575             } finally {
576                 // It's important we cancel all outstanding connect, write and read operations now so
577                 // we will be able to process a delayed close if needed.
578                 cancelOps(cancelConnect);
579             }
580 
581             if (canCloseNow()) {
582                 // Currently there are is no WRITE and READ scheduled so we can start to teardown the channel.
583                 closeNow();
584             }
585         }
586 
587         private boolean cancelOps(boolean cancelConnect) {
588             if (registration == null || !registration.isValid()) {
589                 return false;
590             }
591             boolean cancelled = false;
592             byte flags = (byte) 0;
593             if ((ioState & POLL_RDHUP_SCHEDULED) != 0 && pollRdhupId != 0) {
594                 long id = registration.submit(
595                         IoUringIoOps.newAsyncCancel(flags, pollRdhupId, Native.IORING_OP_POLL_ADD));
596                 assert id != 0;
597                 pollRdhupId = 0;
598                 cancelled = true;
599             }
600             if ((ioState & POLL_IN_SCHEDULED) != 0 && pollInId != 0) {
601                 long id = registration.submit(
602                         IoUringIoOps.newAsyncCancel(flags, pollInId, Native.IORING_OP_POLL_ADD));
603                 assert id != 0;
604                 pollInId = 0;
605                 cancelled = true;
606             }
607             if ((ioState & POLL_OUT_SCHEDULED) != 0 && pollOutId != 0) {
608                 long id = registration.submit(
609                         IoUringIoOps.newAsyncCancel(flags, pollOutId, Native.IORING_OP_POLL_ADD));
610                 assert id != 0;
611                 pollOutId = 0;
612                 cancelled = true;
613             }
614             if (cancelConnect && connectId != 0) {
615                 // Best effort to cancel the already submitted connect request.
616                 long id = registration.submit(IoUringIoOps.newAsyncCancel(flags, connectId, Native.IORING_OP_CONNECT));
617                 assert id != 0;
618                 connectId = 0;
619                 cancelled = true;
620             }
621             if (numOutstandingReads != 0 || numOutstandingWrites != 0) {
622                 cancelled = true;
623             }
624             cancelOutstandingReads(registration, numOutstandingReads);
625             cancelOutstandingWrites(registration, numOutstandingWrites);
626             return cancelled;
627         }
628 
629         private boolean canCloseNow() {
630             // Currently there are is no WRITE and READ scheduled, we can close the channel now without
631             // problems related to re-ordering of completions.
632             return canCloseNow0() && (ioState & (WRITE_SCHEDULED | READ_SCHEDULED)) == 0;
633         }
634 
635         protected boolean canCloseNow0() {
636             return true;
637         }
638 
639         private void closeNow() {
640             super.close(newPromise(), delayedClose.cause, delayedClose.closeCause);
641         }
642 
643         @Override
644         protected final void flush0() {
645             // Flush immediately only when there's no pending flush.
646             // If there's a pending flush operation, event loop will call forceFlush() later,
647             // and thus there's no need to call it now.
648             if ((ioState & POLL_OUT_SCHEDULED) == 0) {
649                 super.flush0();
650             }
651         }
652 
653         private void fulfillConnectPromise(ChannelPromise promise, Throwable cause) {
654             if (promise == null) {
655                 // Closed via cancellation and the promise has been notified already.
656                 return;
657             }
658 
659             // Use tryFailure() instead of setFailure() to avoid the race against cancel().
660             promise.tryFailure(cause);
661             closeIfClosed();
662         }
663 
664         private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
665             if (promise == null) {
666                 // Closed via cancellation and the promise has been notified already.
667                 return;
668             }
669             active = true;
670 
671             if (local == null) {
672                 local = socket.localAddress();
673             }
674             computeRemote();
675 
676             if (isStreamSocket()) {
677                 // Register POLLRDHUP
678                 schedulePollRdHup();
679             }
680 
681             // Get the state as trySuccess() may trigger an ChannelFutureListener that will close the Channel.
682             // We still need to ensure we call fireChannelActive() in this case.
683             boolean active = isActive();
684 
685             // trySuccess() will return false if a user cancelled the connection attempt.
686             boolean promiseSet = promise.trySuccess();
687 
688             // Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
689             // because what happened is what happened.
690             if (!wasActive && active) {
691                 pipeline().fireChannelActive();
692             }
693 
694             // If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
695             if (!promiseSet) {
696                 close(voidPromise());
697             }
698         }
699 
700         @Override
701         public final IoUringRecvByteAllocatorHandle recvBufAllocHandle() {
702             if (allocHandle == null) {
703                 allocHandle = new IoUringRecvByteAllocatorHandle(
704                         (RecvByteBufAllocator.ExtendedHandle) super.recvBufAllocHandle());
705             }
706             return allocHandle;
707         }
708 
709         final void shutdownInput(boolean allDataRead) {
710             logger.trace("shutdownInput Fd: {}", fd().intValue());
711             if (!socket.isInputShutdown()) {
712                 if (isAllowHalfClosure(config())) {
713                     try {
714                         socket.shutdown(true, false);
715                     } catch (IOException ignored) {
716                         // We attempted to shutdown and failed, which means the input has already effectively been
717                         // shutdown.
718                         fireEventAndClose(ChannelInputShutdownEvent.INSTANCE);
719                         return;
720                     } catch (NotYetConnectedException ignore) {
721                         // We attempted to shutdown and failed, which means the input has already effectively been
722                         // shutdown.
723                     }
724                     pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
725                 } else {
726                     // Handle this same way as if we did read all data so we don't schedule another read.
727                     inputClosedSeenErrorOnRead = true;
728                     close(voidPromise());
729                     return;
730                 }
731             }
732             if (allDataRead && !inputClosedSeenErrorOnRead) {
733                 inputClosedSeenErrorOnRead = true;
734                 pipeline().fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
735             }
736         }
737 
738         private void fireEventAndClose(Object evt) {
739             pipeline().fireUserEventTriggered(evt);
740             close(voidPromise());
741         }
742 
743         final void schedulePollIn() {
744             assert (ioState & POLL_IN_SCHEDULED) == 0;
745             if (!isActive() || shouldBreakIoUringInReady(config())) {
746                 return;
747             }
748             pollInId = schedulePollAdd(POLL_IN_SCHEDULED, Native.POLLIN, allowMultiShotPollIn());
749         }
750 
751         private void readComplete(byte op, int res, int flags, short data) {
752             assert numOutstandingReads > 0 || numOutstandingReads == -1 : numOutstandingReads;
753 
754             boolean multishot = numOutstandingReads == -1;
755             boolean rearm = (flags & Native.IORING_CQE_F_MORE) == 0;
756             if (rearm) {
757                 // Reset READ_SCHEDULED if there is nothing more to handle and so we need to re-arm. This works for
758                 // multi-shot and non multi-shot variants.
759                 ioState &= ~READ_SCHEDULED;
760             }
761             boolean pending = readPending;
762             if (multishot) {
763                 // Reset readPending so we can still keep track if we might need to cancel the multi-shot read or
764                 // not.
765                 readPending = false;
766             } else if (--numOutstandingReads == 0) {
767                 // We received all outstanding completions.
768                 readPending = false;
769                 ioState &= ~READ_SCHEDULED;
770             }
771             inReadComplete = true;
772             try {
773                 socketIsEmpty = socketIsEmpty(flags);
774                 socketHasMoreData = IoUring.isCqeFSockNonEmptySupported() &&
775                         (flags & Native.IORING_CQE_F_SOCK_NONEMPTY) != 0;
776                 readComplete0(op, res, flags, data, numOutstandingReads);
777             } finally {
778                 try {
779                     // Check if we should consider the read loop to be done.
780                     if (recvBufAllocHandle().isReadComplete()) {
781                         // Reset the handle as we are done with the read-loop.
782                         recvBufAllocHandle().reset(config());
783 
784                         // Check if this was a readComplete(...) triggered by a read or multi-shot read.
785                         if (!multishot) {
786                             if (readPending) {
787                                 // This was a "normal" read and the user did signal we should continue reading.
788                                 // Let's schedule the read now.
789                                 doBeginReadNow();
790                             }
791                         } else {
792                             // The readComplete(...) was triggered by a multi-shot read. Because of this the state
793                             // machine is a bit more complicated.
794 
795                             if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
796                                 // The readComplete(...) was triggered because the previous read was cancelled.
797                                 // In this case we we need to check if the user did signal the desire to read again
798                                 // in the meantime. If this is the case we need to schedule the read to ensure
799                                 // we do not stall.
800                                 if (pending) {
801                                     doBeginReadNow();
802                                 }
803                             } else if (rearm) {
804                                 // We need to rearm the multishot as otherwise we might miss some data.
805                                 doBeginReadNow();
806                             } else if (!readPending) {
807                                 // Cancel the multi-shot read now as the user did not signal that we want to keep
808                                 // reading while we handle the completion event.
809                                 cancelOutstandingReads(registration, numOutstandingReads);
810                             }
811                         }
812                     } else if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
813                         // The readComplete(...) was triggered because the previous read was cancelled.
814                         // In this case we we need to check if the user did signal the desire to read again
815                         // in the meantime. If this is the case we need to schedule the read to ensure
816                         // we do not stall.
817                         if (pending) {
818                             doBeginReadNow();
819                         }
820                     } else if (multishot && rearm) {
821                         // We need to rearm the multishot as otherwise we might miss some data.
822                         doBeginReadNow();
823                     }
824                 } finally {
825                     inReadComplete = false;
826                     socketIsEmpty = false;
827                 }
828             }
829         }
830 
831         /**
832          * Called once a read was completed.
833          */
834         protected abstract void readComplete0(byte op, int res, int flags, short data, int outstandingCompletes);
835 
836         /**
837          * Called once POLLRDHUP event is ready to be processed
838          */
839         private void pollRdHup(int res) {
840             ioState &= ~POLL_RDHUP_SCHEDULED;
841             pollRdhupId = 0;
842             if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
843                 return;
844             }
845 
846             // Mark that we received a POLLRDHUP and so need to continue reading until all the input ist drained.
847             recvBufAllocHandle().rdHupReceived();
848 
849             if (isActive()) {
850                 scheduleFirstReadIfNeeded();
851             } else {
852                 // Just to be safe make sure the input marked as closed.
853                 shutdownInput(false);
854             }
855         }
856 
857         /**
858          * Called once POLLIN event is ready to be processed
859          */
860         private void pollIn(int res, int flags, short data) {
861             // Check if we need to rearm. This works for both cases, POLL_ADD and POLL_ADD_MULTI.
862             boolean rearm = (flags & Native.IORING_CQE_F_MORE) == 0;
863             if (rearm) {
864                 ioState &= ~POLL_IN_SCHEDULED;
865                 pollInId = 0;
866             }
867             if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
868                 return;
869             }
870             if (!readPending) {
871                 // We received the POLLIN but the user is not interested yet in reading, just mark socketHasMoreData
872                 // as true so we will trigger a read directly once the user calls read()
873                 socketHasMoreData = true;
874                 return;
875             }
876             scheduleFirstReadIfNeeded();
877         }
878 
879         private void scheduleFirstReadIfNeeded() {
880             if ((ioState & READ_SCHEDULED) == 0) {
881                 scheduleFirstRead();
882             }
883         }
884 
885         private void scheduleFirstRead() {
886             // This is a new "read loop" so we need to reset the allocHandle.
887             final ChannelConfig config = config();
888             final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
889             allocHandle.reset(config);
890             scheduleRead(true);
891         }
892 
893         protected final void scheduleRead(boolean first) {
894             // Only schedule another read if the fd is still open.
895             if (delayedClose == null && fd().isOpen() && (ioState & READ_SCHEDULED) == 0) {
896                 numOutstandingReads = (short) scheduleRead0(first, socketIsEmpty);
897                 if (numOutstandingReads > 0 || numOutstandingReads == -1) {
898                     ioState |= READ_SCHEDULED;
899                 }
900             }
901         }
902 
903         /**
904          * Schedule a read and returns the number of {@link #readComplete(byte, int, int, short)}
905          * calls that are expected because of the scheduled read.
906          *
907          * @param first             {@code true} if this is the first read of a read loop.
908          * @param socketIsEmpty     {@code true} if the socket is guaranteed to be empty, {@code false} otherwise.
909          * @return                  the number of {@link #readComplete(byte, int, int, short)} calls expected or
910          *                          {@code -1} if {@link #readComplete(byte, int, int, short)} is called until
911          *                          the read is cancelled (multi-shot).
912          */
913         protected abstract int scheduleRead0(boolean first, boolean socketIsEmpty);
914 
915         /**
916          * Called once POLLOUT event is ready to be processed
917          *
918          * @param res   the result.
919          */
920         private void pollOut(int res) {
921             ioState &= ~POLL_OUT_SCHEDULED;
922             pollOutId = 0;
923             if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
924                 return;
925             }
926             // pending connect
927             if (connectPromise != null) {
928                 // Note this method is invoked by the event loop only if the connection attempt was
929                 // neither cancelled nor timed out.
930 
931                 assert eventLoop().inEventLoop();
932 
933                 boolean connectStillInProgress = false;
934                 try {
935                     boolean wasActive = isActive();
936                     if (!socket.finishConnect()) {
937                         connectStillInProgress = true;
938                         return;
939                     }
940                     fulfillConnectPromise(connectPromise, wasActive);
941                 } catch (Throwable t) {
942                     fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
943                 } finally {
944                     if (!connectStillInProgress) {
945                         // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0
946                         // is used
947                         // See https://github.com/netty/netty/issues/1770
948                         cancelConnectTimeoutFuture();
949                         connectPromise = null;
950                     } else {
951                         // The connect was not done yet, register for POLLOUT again
952                         schedulePollOut();
953                     }
954                 }
955             } else if (!socket.isOutputShutdown()) {
956                 // Try writing again
957                 super.flush0();
958             }
959         }
960 
961         /**
962          * Called once a write was completed.
963          *
964          * @param op    the op code.
965          * @param res   the result.
966          * @param flags the flags.
967          * @param data  the data that was passed when submitting the op.
968          */
969         private void writeComplete(byte op, int res, int flags, short data) {
970             if ((ioState & CONNECT_SCHEDULED) != 0) {
971                 // The writeComplete(...) callback was called because of a sendmsg(...) result that was used for
972                 // TCP_FASTOPEN_CONNECT.
973                 freeMsgHdrArray();
974                 if (res > 0) {
975                     // Connect complete!
976                     outboundBuffer().removeBytes(res);
977 
978                     // Explicit pass in 0 as this is returned by a connect(...) call when it was successful.
979                     connectComplete(op, 0, flags, data);
980                 } else if (res == ERRNO_EINPROGRESS_NEGATIVE || res == 0) {
981                     // This happens when we (as a client) have no pre-existing cookie for doing a fast-open connection.
982                     // In this case, our TCP connection will be established normally, but no data was transmitted at
983                     // this time. We'll just transmit the data with normal writes later.
984                     // Let's submit a normal connect.
985                     submitConnect((InetSocketAddress) requestedRemoteAddress);
986                 } else {
987                     // There was an error, handle it as a normal connect error.
988                     connectComplete(op, res, flags, data);
989                 }
990                 return;
991             }
992 
993             if ((flags & Native.IORING_CQE_F_NOTIF) == 0) {
994                 assert numOutstandingWrites > 0;
995                 --numOutstandingWrites;
996             }
997 
998             boolean writtenAll = writeComplete0(op, res, flags, data, numOutstandingWrites);
999             if (!writtenAll && (ioState & POLL_OUT_SCHEDULED) == 0) {
1000 
1001                 // We were not able to write everything, let's register for POLLOUT
1002                 schedulePollOut();
1003             }
1004 
1005             // We only reset this once we are done with calling removeBytes(...) as otherwise we may trigger a write
1006             // while still removing messages internally in removeBytes(...) which then may corrupt state.
1007             if (numOutstandingWrites == 0) {
1008                 ioState &= ~WRITE_SCHEDULED;
1009 
1010                 // If we could write all and we did not schedule a pollout yet let us try to write again
1011                 if (writtenAll && (ioState & POLL_OUT_SCHEDULED) == 0) {
1012                     scheduleWriteIfNeeded(unsafe().outboundBuffer(), false);
1013                 }
1014             }
1015         }
1016 
1017         /**
1018          * Called once a write was completed.
1019          * @param op            the op code
1020          * @param res           the result.
1021          * @param flags         the flags.
1022          * @param data          the data that was passed when submitting the op.
1023          * @param outstanding   the outstanding write completions.
1024          */
1025         abstract boolean writeComplete0(byte op, int res, int flags, short data, int outstanding);
1026 
1027         /**
1028          * Called once a cancel was completed.
1029          *
1030          * @param op            the op code
1031          * @param res           the result.
1032          * @param flags         the flags.
1033          * @param data          the data that was passed when submitting the op.
1034          */
1035         void cancelComplete0(byte op, int res, int flags, short data) {
1036             // NOOP
1037         }
1038 
1039         /**
1040          * Called once a connect was completed.
1041          * @param op            the op code.
1042          * @param res           the result.
1043          * @param flags         the flags.
1044          * @param data          the data that was passed when submitting the op.
1045          */
1046         void connectComplete(byte op, int res, int flags, short data) {
1047             ioState &= ~CONNECT_SCHEDULED;
1048             freeRemoteAddressMemory();
1049 
1050             if (res == ERRNO_EINPROGRESS_NEGATIVE || res == ERROR_EALREADY_NEGATIVE) {
1051                 // connect not complete yet need to wait for poll_out event
1052                 schedulePollOut();
1053             } else {
1054                 try {
1055                     if (res == 0) {
1056                         fulfillConnectPromise(connectPromise, active);
1057                         if (readPending) {
1058                             doBeginReadNow();
1059                         }
1060                     } else {
1061                         try {
1062                             Errors.throwConnectException("io_uring connect", res);
1063                         } catch (Throwable cause) {
1064                             fulfillConnectPromise(connectPromise, cause);
1065                         }
1066                     }
1067                 } finally {
1068                     // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is
1069                     // used
1070                     // See https://github.com/netty/netty/issues/1770
1071                     cancelConnectTimeoutFuture();
1072                     connectPromise = null;
1073                 }
1074             }
1075         }
1076 
1077         @Override
1078         public void connect(
1079                 final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
1080             // Don't mark the connect promise as uncancellable as in fact we can cancel it as it is using
1081             // non-blocking io.
1082             if (promise.isDone() || !ensureOpen(promise)) {
1083                 return;
1084             }
1085 
1086             if (delayedClose != null) {
1087                 promise.tryFailure(annotateConnectException(new ClosedChannelException(), remoteAddress));
1088                 return;
1089             }
1090             try {
1091                 if (connectPromise != null) {
1092                     throw new ConnectionPendingException();
1093                 }
1094                 if (localAddress instanceof InetSocketAddress) {
1095                     checkResolvable((InetSocketAddress) localAddress);
1096                 }
1097 
1098                 if (remoteAddress instanceof InetSocketAddress) {
1099                     checkResolvable((InetSocketAddress) remoteAddress);
1100                 }
1101 
1102                 if (remote != null) {
1103                     // Check if already connected before trying to connect. This is needed as connect(...) will not#
1104                     // return -1 and set errno to EISCONN if a previous connect(...) attempt was setting errno to
1105                     // EINPROGRESS and finished later.
1106                     throw new AlreadyConnectedException();
1107                 }
1108 
1109                 if (localAddress != null) {
1110                     socket.bind(localAddress);
1111                 }
1112 
1113                 if (remoteAddress instanceof InetSocketAddress) {
1114                     InetSocketAddress inetSocketAddress = (InetSocketAddress) remoteAddress;
1115                     ByteBuf initialData = null;
1116                     if (IoUring.isTcpFastOpenClientSideAvailable() &&
1117                         config().getOption(ChannelOption.TCP_FASTOPEN_CONNECT) == Boolean.TRUE) {
1118                         ChannelOutboundBuffer outbound = unsafe().outboundBuffer();
1119                         outbound.addFlush();
1120                         Object curr;
1121                         if ((curr = outbound.current()) instanceof ByteBuf) {
1122                             initialData = (ByteBuf) curr;
1123                         }
1124                     }
1125                     if (initialData != null) {
1126                         msgHdrMemoryArray = new MsgHdrMemoryArray((short) 1);
1127                         MsgHdrMemory hdr = msgHdrMemoryArray.hdr(0);
1128                         fillTFOInitData(hdr, inetSocketAddress, initialData);
1129 
1130                         int fd = fd().intValue();
1131                         IoRegistration registration = registration();
1132                         IoUringIoOps ops = IoUringIoOps.newSendmsg(fd, (byte) 0, Native.MSG_FASTOPEN,
1133                                 hdr.address(), hdr.idx());
1134                         connectId = registration.submit(ops);
1135                         if (connectId == 0) {
1136                             // Directly release the memory if submitting failed.
1137                             freeMsgHdrArray();
1138                         }
1139                     } else {
1140                         submitConnect(inetSocketAddress);
1141                     }
1142                 } else if (remoteAddress instanceof DomainSocketAddress) {
1143                     DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) remoteAddress;
1144                     submitConnect(unixDomainSocketAddress);
1145                 } else {
1146                     throw new Error("Unexpected SocketAddress implementation " + className(remoteAddress));
1147                 }
1148 
1149                 if (connectId != 0) {
1150                     ioState |= CONNECT_SCHEDULED;
1151                 }
1152             } catch (Throwable t) {
1153                 closeIfClosed();
1154                 promise.tryFailure(annotateConnectException(t, remoteAddress));
1155                 return;
1156             }
1157             connectPromise = promise;
1158             requestedRemoteAddress = remoteAddress;
1159             // Schedule connect timeout.
1160             int connectTimeoutMillis = config().getConnectTimeoutMillis();
1161             if (connectTimeoutMillis > 0) {
1162                 connectTimeoutFuture = eventLoop().schedule(new Runnable() {
1163                     @Override
1164                     public void run() {
1165                         ChannelPromise connectPromise = AbstractIoUringChannel.this.connectPromise;
1166                         if (connectPromise != null && !connectPromise.isDone() &&
1167                                 connectPromise.tryFailure(new ConnectTimeoutException(
1168                                         "connection timed out: " + remoteAddress))) {
1169                             close(voidPromise());
1170                         }
1171                     }
1172                 }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
1173             }
1174 
1175             promise.addListener(new ChannelFutureListener() {
1176                 @Override
1177                 public void operationComplete(ChannelFuture future) {
1178                     // If the connect future is cancelled we also cancel the timeout and close the
1179                     // underlying socket.
1180                     if (future.isCancelled()) {
1181                         cancelConnectTimeoutFuture();
1182                         connectPromise = null;
1183                         close(voidPromise());
1184                     }
1185                 }
1186             });
1187         }
1188 
1189         private void fillTFOInitData(MsgHdrMemory hdr, InetSocketAddress inetSocketAddress,
1190                                      ByteBuf initialData) throws Exception {
1191            if (initialData.hasMemoryAddress()) {
1192                hdr.set(socket, inetSocketAddress,
1193                        initialData.memoryAddress() + initialData.readerIndex(),
1194                        initialData.readableBytes(), (short) 0);
1195            } else {
1196                // Use an iovec array for CompositeByteBuf and other buffers without a memory address.
1197                // If the shared IovArray has not enough space, the rest is sent after connect.
1198                IoUringIoHandler handler = registration().attachment();
1199                IovArray iovArray = handler.iovArray();
1200                int iovOffset = iovArray.count();
1201                iovArray.processMessage(initialData);
1202                long iovArrayAddress = iovArray.memoryAddress(iovOffset);
1203                int iovArrayLength = iovArray.count() - iovOffset;
1204                hdr.setWithIovArrayAddress(socket, inetSocketAddress, iovArrayAddress, iovArrayLength, (short) 0);
1205            }
1206         }
1207 
1208         @Override
1209         public final void deregister(ChannelPromise promise) {
1210             if (deregisterPromise != null) {
1211                 // A deregistration is already in progress.
1212                 PromiseNotifier.cascade(deregisterPromise, promise);
1213             } else if (!isRegistered()) {
1214                 promise.setSuccess();
1215             } else {
1216                 // We need to store a reference to the original promise as we should only notify it once we
1217                 // have handles all pending completions.
1218                 deregisterPromise = promise;
1219                 super.deregister(newPromise().addListener(f -> {
1220                     if (!f.isSuccess()) {
1221                         this.deregisterPromise = null;
1222                         promise.setFailure(f.cause());
1223                     }
1224                 }));
1225             }
1226         }
1227     }
1228 
1229     private void submitConnect(InetSocketAddress inetSocketAddress) {
1230         cleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_SOCKADDR_STORAGE);
1231         remoteAddressMemory = cleanable.buffer();
1232 
1233         SockaddrIn.set(socket.isIpv6(), remoteAddressMemory, inetSocketAddress);
1234 
1235         int fd = fd().intValue();
1236         IoRegistration registration = registration();
1237         IoUringIoOps ops = IoUringIoOps.newConnect(
1238                 fd, (byte) 0, Buffer.memoryAddress(remoteAddressMemory), nextOpsId());
1239         connectId = registration.submit(ops);
1240         if (connectId == 0) {
1241             // Directly release the memory if submitting failed.
1242             freeRemoteAddressMemory();
1243         }
1244     }
1245 
1246     private void submitConnect(DomainSocketAddress unixDomainSocketAddress) {
1247         cleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_SOCKADDR_UN);
1248         remoteAddressMemory = cleanable.buffer();
1249         int addrLen = SockaddrIn.setUds(remoteAddressMemory, unixDomainSocketAddress);
1250         int fd = fd().intValue();
1251         IoRegistration registration = registration();
1252         long addr = Buffer.memoryAddress(remoteAddressMemory);
1253         IoUringIoOps ops = IoUringIoOps.newConnect(fd, (byte) 0, addr, addrLen, nextOpsId());
1254         connectId = registration.submit(ops);
1255         if (connectId == 0) {
1256             // Directly release the memory if submitting failed.
1257             freeRemoteAddressMemory();
1258         }
1259     }
1260 
1261     @Override
1262     protected Object filterOutboundMessage(Object msg) {
1263         if (msg instanceof ByteBuf) {
1264             ByteBuf buf = (ByteBuf) msg;
1265             return UnixChannelUtil.isBufferCopyNeededForWrite(buf)? newDirectBuffer(buf) : buf;
1266         }
1267         throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg));
1268     }
1269 
1270     @Override
1271     protected void doRegister(ChannelPromise promise) {
1272         IoEventLoop eventLoop = (IoEventLoop) eventLoop();
1273         eventLoop.register(ioUringUnsafe()).addListener(f -> {
1274             if (f.isSuccess()) {
1275                 registration = (IoRegistration) f.getNow();
1276                 promise.setSuccess();
1277             } else {
1278                 promise.setFailure(f.cause());
1279             }
1280         });
1281     }
1282 
1283     @Override
1284     protected final void doDeregister() {
1285         // Cancel all previous submitted ops.
1286         if (!ioUringUnsafe().cancelOps(connectPromise != null)) {
1287             // It's possible that we never registered anything and so we did not submit any ASYNC_CANCEL.
1288             // In this case directly call cancel as we will not receive any completion at all.
1289             if (registration != null) {
1290                 registration.cancel();
1291             }
1292         }
1293     }
1294 
1295     @Override
1296     protected void doBind(final SocketAddress local) throws Exception {
1297         if (local instanceof InetSocketAddress) {
1298             checkResolvable((InetSocketAddress) local);
1299         }
1300         socket.bind(local);
1301         this.local = socket.localAddress();
1302     }
1303 
1304     protected static void checkResolvable(InetSocketAddress addr) {
1305         if (addr.isUnresolved()) {
1306             throw new UnresolvedAddressException();
1307         }
1308     }
1309 
1310     @Override
1311     protected final SocketAddress localAddress0() {
1312         return local;
1313     }
1314 
1315     @Override
1316     protected final SocketAddress remoteAddress0() {
1317         return remote;
1318     }
1319 
1320     private static boolean isAllowHalfClosure(ChannelConfig config) {
1321         return config instanceof SocketChannelConfig &&
1322                ((SocketChannelConfig) config).isAllowHalfClosure();
1323     }
1324 
1325     private void cancelConnectTimeoutFuture() {
1326         if (connectTimeoutFuture != null) {
1327             connectTimeoutFuture.cancel(false);
1328             connectTimeoutFuture = null;
1329         }
1330     }
1331 
1332     private void computeRemote() {
1333         if (requestedRemoteAddress instanceof InetSocketAddress) {
1334             remote = computeRemoteAddr((InetSocketAddress) requestedRemoteAddress, socket.remoteAddress());
1335         }
1336     }
1337 
1338     private boolean shouldBreakIoUringInReady(ChannelConfig config) {
1339         return socket.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
1340     }
1341 
1342     /**
1343      * Return if the socket is guaranteed to be empty when the submitted io was executed and the completion event be
1344      * created.
1345      * @param flags     the flags that were part of the completion
1346      * @return          {@code true} if empty.
1347      */
1348     protected abstract boolean socketIsEmpty(int flags);
1349 
1350     abstract boolean isPollInFirst();
1351 }