1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.nio;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.buffer.ByteBufUtil;
21 import io.netty.buffer.Unpooled;
22 import io.netty.channel.AbstractChannel;
23 import io.netty.channel.Channel;
24 import io.netty.channel.ChannelException;
25 import io.netty.channel.ChannelFuture;
26 import io.netty.channel.ChannelFutureListener;
27 import io.netty.channel.ChannelPromise;
28 import io.netty.channel.ConnectTimeoutException;
29 import io.netty.channel.EventLoop;
30 import io.netty.channel.IoEvent;
31 import io.netty.channel.IoEventLoop;
32 import io.netty.channel.IoEventLoopGroup;
33 import io.netty.channel.IoRegistration;
34 import io.netty.util.ReferenceCountUtil;
35 import io.netty.util.ReferenceCounted;
36 import io.netty.util.concurrent.Future;
37 import io.netty.util.internal.ObjectUtil;
38 import io.netty.util.internal.logging.InternalLogger;
39 import io.netty.util.internal.logging.InternalLoggerFactory;
40
41 import java.io.IOException;
42 import java.net.SocketAddress;
43 import java.nio.channels.CancelledKeyException;
44 import java.nio.channels.ClosedChannelException;
45 import java.nio.channels.ConnectionPendingException;
46 import java.nio.channels.SelectableChannel;
47 import java.nio.channels.SelectionKey;
48 import java.util.concurrent.TimeUnit;
49
50
51
52
53 public abstract class AbstractNioChannel extends AbstractChannel {
54
55 private static final InternalLogger logger =
56 InternalLoggerFactory.getInstance(AbstractNioChannel.class);
57
58 private final SelectableChannel ch;
59 protected final int readInterestOp;
60 protected final NioIoOps readOps;
61 volatile IoRegistration registration;
62 boolean readPending;
63 private final Runnable clearReadPendingRunnable = new Runnable() {
64 @Override
65 public void run() {
66 clearReadPending0();
67 }
68 };
69
70
71
72
73
74 private ChannelPromise connectPromise;
75 private Future<?> connectTimeoutFuture;
76 private SocketAddress requestedRemoteAddress;
77
78
79
80
81
82
83
84
85 protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readOps) {
86 this(parent, ch, NioIoOps.valueOf(readOps));
87 }
88
89 protected AbstractNioChannel(Channel parent, SelectableChannel ch, NioIoOps readOps) {
90 super(parent);
91 this.ch = ch;
92 this.readInterestOp = ObjectUtil.checkNotNull(readOps, "readOps").value;
93 this.readOps = readOps;
94 try {
95 ch.configureBlocking(false);
96 } catch (IOException e) {
97 try {
98 ch.close();
99 } catch (IOException e2) {
100 logger.warn(
101 "Failed to close a partially initialized socket.", e2);
102 }
103
104 throw new ChannelException("Failed to enter non-blocking mode.", e);
105 }
106 }
107
108 protected void addAndSubmit(NioIoOps addOps) {
109 int interestOps = selectionKey().interestOps();
110 if (!addOps.isIncludedIn(interestOps)) {
111 try {
112 registration().submit(NioIoOps.valueOf(interestOps).with(addOps));
113 } catch (Exception e) {
114 throw new ChannelException(e);
115 }
116 }
117 }
118
119 protected void removeAndSubmit(NioIoOps removeOps) {
120 int interestOps = selectionKey().interestOps();
121 if (removeOps.isIncludedIn(interestOps)) {
122 try {
123 registration().submit(NioIoOps.valueOf(interestOps).without(removeOps));
124 } catch (Exception e) {
125 throw new ChannelException(e);
126 }
127 }
128 }
129
130 @Override
131 public boolean isOpen() {
132 return ch.isOpen();
133 }
134
135 @Override
136 public NioUnsafe unsafe() {
137 return (NioUnsafe) super.unsafe();
138 }
139
140 protected SelectableChannel javaChannel() {
141 return ch;
142 }
143
144
145
146
147
148
149 @Deprecated
150 protected SelectionKey selectionKey() {
151 return registration().attachment();
152 }
153
154 @SuppressWarnings("unchecked")
155 protected IoRegistration registration() {
156 assert registration != null;
157 return registration;
158 }
159
160
161
162
163
164 @Deprecated
165 protected boolean isReadPending() {
166 return readPending;
167 }
168
169
170
171
172
173 @Deprecated
174 protected void setReadPending(final boolean readPending) {
175 if (isRegistered()) {
176 EventLoop eventLoop = eventLoop();
177 if (eventLoop.inEventLoop()) {
178 setReadPending0(readPending);
179 } else {
180 eventLoop.execute(new Runnable() {
181 @Override
182 public void run() {
183 setReadPending0(readPending);
184 }
185 });
186 }
187 } else {
188
189
190
191 this.readPending = readPending;
192 }
193 }
194
195
196
197
198 protected final void clearReadPending() {
199 if (isRegistered()) {
200 EventLoop eventLoop = eventLoop();
201 if (eventLoop.inEventLoop()) {
202 clearReadPending0();
203 } else {
204 eventLoop.execute(clearReadPendingRunnable);
205 }
206 } else {
207
208
209
210 readPending = false;
211 }
212 }
213
214 private void setReadPending0(boolean readPending) {
215 this.readPending = readPending;
216 if (!readPending) {
217 ((AbstractNioUnsafe) unsafe()).removeReadOp();
218 }
219 }
220
221 private void clearReadPending0() {
222 readPending = false;
223 ((AbstractNioUnsafe) unsafe()).removeReadOp();
224 }
225
226
227
228
229 public interface NioUnsafe extends Unsafe {
230
231
232
233 SelectableChannel ch();
234
235
236
237
238 void finishConnect();
239
240
241
242
243 void read();
244
245 void forceFlush();
246 }
247
248 protected abstract class AbstractNioUnsafe extends AbstractUnsafe implements NioUnsafe, NioIoHandle {
249 @Override
250 public void close() {
251 close(voidPromise());
252 }
253
254 @Override
255 public SelectableChannel selectableChannel() {
256 return ch();
257 }
258
259 Channel channel() {
260 return AbstractNioChannel.this;
261 }
262
263 protected final void removeReadOp() {
264
265
266
267 IoRegistration registration = AbstractNioChannel.this.registration;
268
269
270
271 if (registration == null || !registration.isValid()) {
272 return;
273 }
274 removeAndSubmit(readOps);
275 }
276
277 @Override
278 public final SelectableChannel ch() {
279 return javaChannel();
280 }
281
282 @Override
283 public final void connect(
284 final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
285
286
287 if (promise.isDone() || !ensureOpen(promise)) {
288 return;
289 }
290
291 try {
292 if (connectPromise != null) {
293
294 throw new ConnectionPendingException();
295 }
296
297 boolean wasActive = isActive();
298 if (doConnect(remoteAddress, localAddress)) {
299 fulfillConnectPromise(promise, wasActive);
300 } else {
301 connectPromise = promise;
302 requestedRemoteAddress = remoteAddress;
303
304
305 final int connectTimeoutMillis = config().getConnectTimeoutMillis();
306 if (connectTimeoutMillis > 0) {
307 connectTimeoutFuture = eventLoop().schedule(new Runnable() {
308 @Override
309 public void run() {
310 ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
311 if (connectPromise != null && !connectPromise.isDone()
312 && connectPromise.tryFailure(new ConnectTimeoutException(
313 "connection timed out after " + connectTimeoutMillis + " ms: " +
314 remoteAddress))) {
315 close(voidPromise());
316 }
317 }
318 }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
319 }
320
321 promise.addListener(new ChannelFutureListener() {
322 @Override
323 public void operationComplete(ChannelFuture future) {
324
325
326 if (future.isCancelled()) {
327 if (connectTimeoutFuture != null) {
328 connectTimeoutFuture.cancel(false);
329 }
330 connectPromise = null;
331 close(voidPromise());
332 }
333 }
334 });
335 }
336 } catch (Throwable t) {
337 promise.tryFailure(annotateConnectException(t, remoteAddress));
338 closeIfClosed();
339 }
340 }
341
342 private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
343 if (promise == null) {
344
345 return;
346 }
347
348
349
350 boolean active = isActive();
351
352
353 boolean promiseSet = promise.trySuccess();
354
355
356
357 if (!wasActive && active) {
358 pipeline().fireChannelActive();
359 }
360
361
362 if (!promiseSet) {
363 close(voidPromise());
364 }
365 }
366
367 private void fulfillConnectPromise(ChannelPromise promise, Throwable cause) {
368 if (promise == null) {
369
370 return;
371 }
372
373
374 promise.tryFailure(cause);
375 closeIfClosed();
376 }
377
378 @Override
379 public final void finishConnect() {
380
381
382
383 assert eventLoop().inEventLoop();
384
385 try {
386 boolean wasActive = isActive();
387 doFinishConnect();
388 fulfillConnectPromise(connectPromise, wasActive);
389 } catch (Throwable t) {
390 fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
391 } finally {
392
393
394 if (connectTimeoutFuture != null) {
395 connectTimeoutFuture.cancel(false);
396 }
397 connectPromise = null;
398 }
399 }
400
401 @Override
402 protected final void flush0() {
403
404
405
406 if (!isFlushPending()) {
407 super.flush0();
408 }
409 }
410
411 @Override
412 public final void forceFlush() {
413
414 super.flush0();
415 }
416
417 private boolean isFlushPending() {
418 IoRegistration registration = registration();
419 return registration.isValid() && NioIoOps.WRITE.isIncludedIn((
420 (SelectionKey) registration.attachment()).interestOps());
421 }
422
423 @Override
424 public void handle(IoRegistration registration, IoEvent event) {
425 try {
426 NioIoEvent nioEvent = (NioIoEvent) event;
427 NioIoOps nioReadyOps = nioEvent.ops();
428
429
430 if (nioReadyOps.contains(NioIoOps.CONNECT)) {
431
432
433 removeAndSubmit(NioIoOps.CONNECT);
434
435 unsafe().finishConnect();
436 }
437
438
439 if (nioReadyOps.contains(NioIoOps.WRITE)) {
440
441
442 forceFlush();
443 }
444
445
446
447 if (nioReadyOps.contains(NioIoOps.READ_AND_ACCEPT) || nioReadyOps.equals(NioIoOps.NONE)) {
448 read();
449 }
450 } catch (CancelledKeyException ignored) {
451 close(voidPromise());
452 }
453 }
454 }
455
456 @Override
457 protected boolean isCompatible(EventLoop loop) {
458 return loop instanceof IoEventLoop && ((IoEventLoopGroup) loop).isCompatible(AbstractNioUnsafe.class);
459 }
460
461 @SuppressWarnings("unchecked")
462 @Override
463 protected void doRegister(ChannelPromise promise) {
464 assert registration == null;
465 ((IoEventLoop) eventLoop()).register((AbstractNioUnsafe) unsafe()).addListener(f -> {
466 if (f.isSuccess()) {
467 registration = (IoRegistration) f.getNow();
468 promise.setSuccess();
469 } else {
470 promise.setFailure(f.cause());
471 }
472 });
473 }
474
475 @Override
476 protected void doDeregister() throws Exception {
477 IoRegistration registration = this.registration;
478 if (registration != null) {
479 this.registration = null;
480 registration.cancel();
481 }
482 }
483
484 @Override
485 protected void doBeginRead() throws Exception {
486
487 IoRegistration registration = this.registration;
488 if (registration == null || !registration.isValid()) {
489 return;
490 }
491
492 readPending = true;
493
494 addAndSubmit(readOps);
495 }
496
497
498
499
500 protected abstract boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception;
501
502
503
504
505 protected abstract void doFinishConnect() throws Exception;
506
507
508
509
510
511
512 protected final ByteBuf newDirectBuffer(ByteBuf buf) {
513 final int readableBytes = buf.readableBytes();
514 if (readableBytes == 0) {
515 ReferenceCountUtil.safeRelease(buf);
516 return Unpooled.EMPTY_BUFFER;
517 }
518
519 final ByteBufAllocator alloc = alloc();
520 if (alloc.isDirectBufferPooled()) {
521 ByteBuf directBuf = alloc.directBuffer(readableBytes);
522 directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
523 ReferenceCountUtil.safeRelease(buf);
524 return directBuf;
525 }
526
527 final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
528 if (directBuf != null) {
529 directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
530 ReferenceCountUtil.safeRelease(buf);
531 return directBuf;
532 }
533
534
535 return buf;
536 }
537
538
539
540
541
542
543
544 protected final ByteBuf newDirectBuffer(ReferenceCounted holder, ByteBuf buf) {
545 final int readableBytes = buf.readableBytes();
546 if (readableBytes == 0) {
547 ReferenceCountUtil.safeRelease(holder);
548 return Unpooled.EMPTY_BUFFER;
549 }
550
551 final ByteBufAllocator alloc = alloc();
552 if (alloc.isDirectBufferPooled()) {
553 ByteBuf directBuf = alloc.directBuffer(readableBytes);
554 directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
555 ReferenceCountUtil.safeRelease(holder);
556 return directBuf;
557 }
558
559 final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
560 if (directBuf != null) {
561 directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
562 ReferenceCountUtil.safeRelease(holder);
563 return directBuf;
564 }
565
566
567 if (holder != buf) {
568
569 buf.retain();
570 ReferenceCountUtil.safeRelease(holder);
571 }
572
573 return buf;
574 }
575
576 @Override
577 protected void doClose() throws Exception {
578 ChannelPromise promise = connectPromise;
579 if (promise != null) {
580
581 promise.tryFailure(new ClosedChannelException());
582 connectPromise = null;
583 }
584
585 Future<?> future = connectTimeoutFuture;
586 if (future != null) {
587 future.cancel(false);
588 connectTimeoutFuture = null;
589 }
590 }
591 }