1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.kqueue;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.channel.Channel;
21 import io.netty.channel.ChannelConfig;
22 import io.netty.channel.ChannelFuture;
23 import io.netty.channel.ChannelFutureListener;
24 import io.netty.channel.ChannelMetadata;
25 import io.netty.channel.ChannelOutboundBuffer;
26 import io.netty.channel.ChannelPipeline;
27 import io.netty.channel.ChannelPromise;
28 import io.netty.channel.DefaultFileRegion;
29 import io.netty.channel.EventLoop;
30 import io.netty.channel.FileRegion;
31 import io.netty.channel.internal.ChannelUtils;
32 import io.netty.channel.socket.DuplexChannel;
33 import io.netty.channel.unix.IovArray;
34 import io.netty.channel.unix.SocketWritableByteChannel;
35 import io.netty.channel.unix.UnixChannelUtil;
36 import io.netty.util.LeakPresenceDetector;
37 import io.netty.util.internal.StringUtil;
38 import io.netty.util.internal.UnstableApi;
39 import io.netty.util.internal.logging.InternalLogger;
40 import io.netty.util.internal.logging.InternalLoggerFactory;
41
42 import java.io.IOException;
43 import java.net.SocketAddress;
44 import java.nio.ByteBuffer;
45 import java.nio.channels.WritableByteChannel;
46 import java.util.concurrent.Executor;
47
48 import static io.netty.channel.internal.ChannelUtils.MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD;
49 import static io.netty.channel.internal.ChannelUtils.WRITE_STATUS_SNDBUF_FULL;
50 import static io.netty.util.internal.StringUtil.className;
51
52 public abstract class AbstractKQueueStreamChannel extends AbstractKQueueChannel implements DuplexChannel {
53 private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractKQueueStreamChannel.class);
54 private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
55 private static final String EXPECTED_TYPES =
56 " (expected: " + StringUtil.simpleClassName(ByteBuf.class) + ", " +
57 StringUtil.simpleClassName(DefaultFileRegion.class) + ')';
58 private WritableByteChannel byteChannel;
59 private final Runnable flushTask = new Runnable() {
60 @Override
61 public void run() {
62
63
64 ((AbstractKQueueUnsafe) unsafe()).flush0();
65 }
66 };
67
68 AbstractKQueueStreamChannel(Channel parent, BsdSocket fd, boolean active) {
69 super(parent, fd, active);
70 }
71
72 AbstractKQueueStreamChannel(Channel parent, BsdSocket fd, SocketAddress remote) {
73 super(parent, fd, remote);
74 }
75
76 AbstractKQueueStreamChannel(BsdSocket fd) {
77 this(null, fd, isSoErrorZero(fd));
78 }
79
80 @Override
81 protected AbstractKQueueUnsafe newUnsafe() {
82 return new KQueueStreamUnsafe();
83 }
84
85 @Override
86 public ChannelMetadata metadata() {
87 return METADATA;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private int writeBytes(ChannelOutboundBuffer in, ByteBuf buf) throws Exception {
105 int readableBytes = buf.readableBytes();
106 if (readableBytes == 0) {
107 in.remove();
108 return 0;
109 }
110
111 if (buf.hasMemoryAddress() || buf.nioBufferCount() == 1) {
112 return doWriteBytes(in, buf);
113 } else {
114 ByteBuffer[] nioBuffers = buf.nioBuffers();
115 return writeBytesMultiple(in, nioBuffers, nioBuffers.length, readableBytes,
116 config().getMaxBytesPerGatheringWrite());
117 }
118 }
119
120 private void adjustMaxBytesPerGatheringWrite(long attempted, long written, long oldMaxBytesPerGatheringWrite) {
121
122
123
124 if (attempted == written) {
125 if (attempted << 1 > oldMaxBytesPerGatheringWrite) {
126 config().setMaxBytesPerGatheringWrite(attempted << 1);
127 }
128 } else if (attempted > MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD && written < attempted >>> 1) {
129 config().setMaxBytesPerGatheringWrite(attempted >>> 1);
130 }
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 private int writeBytesMultiple(ChannelOutboundBuffer in, IovArray array) throws IOException {
149 final long expectedWrittenBytes = array.size();
150 assert expectedWrittenBytes != 0;
151 final int cnt = array.count();
152 assert cnt != 0;
153
154 final long localWrittenBytes = socket.writevAddresses(array.memoryAddress(0), cnt);
155 if (localWrittenBytes > 0) {
156 adjustMaxBytesPerGatheringWrite(expectedWrittenBytes, localWrittenBytes, array.maxBytes());
157 in.removeBytes(localWrittenBytes);
158 return 1;
159 }
160 return WRITE_STATUS_SNDBUF_FULL;
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 private int writeBytesMultiple(
182 ChannelOutboundBuffer in, ByteBuffer[] nioBuffers, int nioBufferCnt, long expectedWrittenBytes,
183 long maxBytesPerGatheringWrite) throws IOException {
184 assert expectedWrittenBytes != 0;
185 if (expectedWrittenBytes > maxBytesPerGatheringWrite) {
186 expectedWrittenBytes = maxBytesPerGatheringWrite;
187 }
188
189 final long localWrittenBytes = socket.writev(nioBuffers, 0, nioBufferCnt, expectedWrittenBytes);
190 if (localWrittenBytes > 0) {
191 adjustMaxBytesPerGatheringWrite(expectedWrittenBytes, localWrittenBytes, maxBytesPerGatheringWrite);
192 in.removeBytes(localWrittenBytes);
193 return 1;
194 }
195 return WRITE_STATUS_SNDBUF_FULL;
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion region) throws Exception {
213 final long regionCount = region.count();
214 final long offset = region.transferred();
215
216 if (offset >= regionCount) {
217 in.remove();
218 return 0;
219 }
220
221 final long flushedAmount = socket.sendFile(region, region.position(), offset, regionCount - offset);
222 if (flushedAmount > 0) {
223 in.progress(flushedAmount);
224 if (region.transferred() >= regionCount) {
225 in.remove();
226 }
227 return 1;
228 } else if (flushedAmount == 0) {
229 validateFileRegion(region, offset);
230 }
231 return WRITE_STATUS_SNDBUF_FULL;
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 private int writeFileRegion(ChannelOutboundBuffer in, FileRegion region) throws Exception {
249 if (region.transferred() >= region.count()) {
250 in.remove();
251 return 0;
252 }
253
254 if (byteChannel == null) {
255 byteChannel = new KQueueSocketWritableByteChannel();
256 }
257 final long flushedAmount = region.transferTo(byteChannel, region.transferred());
258 if (flushedAmount > 0) {
259 in.progress(flushedAmount);
260 if (region.transferred() >= region.count()) {
261 in.remove();
262 }
263 return 1;
264 }
265 return WRITE_STATUS_SNDBUF_FULL;
266 }
267
268 @Override
269 protected void doWrite(ChannelOutboundBuffer in) throws Exception {
270
271 removeFastOpenData();
272 int writeSpinCount = config().getWriteSpinCount();
273 do {
274 final int msgCount = in.size();
275
276 if (msgCount > 1 && in.current() instanceof ByteBuf) {
277 writeSpinCount -= doWriteMultiple(in);
278 } else if (msgCount == 0) {
279
280 writeFilter(false);
281
282 return;
283 } else {
284 writeSpinCount -= doWriteSingle(in);
285 }
286
287
288
289
290 } while (writeSpinCount > 0);
291
292 if (writeSpinCount == 0) {
293
294
295
296
297 writeFilter(false);
298
299
300 eventLoop().execute(flushTask);
301 } else {
302
303
304 writeFilter(true);
305 }
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322 protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception {
323
324 Object msg = in.current();
325 if (msg instanceof ByteBuf) {
326 return writeBytes(in, (ByteBuf) msg);
327 } else if (msg instanceof DefaultFileRegion) {
328 return writeDefaultFileRegion(in, (DefaultFileRegion) msg);
329 } else if (msg instanceof FileRegion) {
330 return writeFileRegion(in, (FileRegion) msg);
331 } else {
332
333 throw new Error("Unexpected message type: " + className(msg));
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 private int doWriteMultiple(ChannelOutboundBuffer in) throws Exception {
352 final long maxBytesPerGatheringWrite = config().getMaxBytesPerGatheringWrite();
353 IovArray array = ((NativeArrays) registration().attachment()).cleanIovArray();
354 array.maxBytes(maxBytesPerGatheringWrite);
355 in.forEachFlushedMessage(array);
356
357 if (array.count() >= 1) {
358
359 return writeBytesMultiple(in, array);
360 }
361
362 in.removeBytes(0);
363 return 0;
364 }
365
366 @Override
367 protected Object filterOutboundMessage(Object msg) {
368 if (msg instanceof ByteBuf) {
369 ByteBuf buf = (ByteBuf) msg;
370 return UnixChannelUtil.isBufferCopyNeededForWrite(buf)? newDirectBuffer(buf) : buf;
371 }
372
373 if (msg instanceof FileRegion) {
374 return msg;
375 }
376
377 throw new UnsupportedOperationException(
378 "unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
379 }
380
381 @UnstableApi
382 @Override
383 protected final void doShutdownOutput() throws Exception {
384
385 removeFastOpenData();
386 socket.shutdown(false, true);
387 }
388
389 @Override
390 public boolean isOutputShutdown() {
391 return socket.isOutputShutdown();
392 }
393
394 @Override
395 public boolean isInputShutdown() {
396 return socket.isInputShutdown();
397 }
398
399 @Override
400 public boolean isShutdown() {
401 return socket.isShutdown();
402 }
403
404 @Override
405 public ChannelFuture shutdownOutput() {
406 return shutdownOutput(newPromise());
407 }
408
409 @Override
410 public ChannelFuture shutdownOutput(final ChannelPromise promise) {
411 EventLoop loop = eventLoop();
412 if (loop.inEventLoop()) {
413 ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
414 } else {
415 loop.execute(new Runnable() {
416 @Override
417 public void run() {
418 ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
419 }
420 });
421 }
422 return promise;
423 }
424
425 @Override
426 public ChannelFuture shutdownInput() {
427 return shutdownInput(newPromise());
428 }
429
430 @Override
431 public ChannelFuture shutdownInput(final ChannelPromise promise) {
432 EventLoop loop = eventLoop();
433 if (loop.inEventLoop()) {
434 shutdownInput0(promise);
435 } else {
436 loop.execute(new Runnable() {
437 @Override
438 public void run() {
439 shutdownInput0(promise);
440 }
441 });
442 }
443 return promise;
444 }
445
446 private void shutdownInput0(ChannelPromise promise) {
447 try {
448 socket.shutdown(true, false);
449 } catch (Throwable cause) {
450 promise.setFailure(cause);
451 return;
452 }
453 promise.setSuccess();
454 }
455
456 @Override
457 public ChannelFuture shutdown() {
458 return shutdown(newPromise());
459 }
460
461 @Override
462 public ChannelFuture shutdown(final ChannelPromise promise) {
463 ChannelFuture shutdownOutputFuture = shutdownOutput();
464 if (shutdownOutputFuture.isDone()) {
465 shutdownOutputDone(shutdownOutputFuture, promise);
466 } else {
467 shutdownOutputFuture.addListener(new ChannelFutureListener() {
468 @Override
469 public void operationComplete(final ChannelFuture shutdownOutputFuture) throws Exception {
470 shutdownOutputDone(shutdownOutputFuture, promise);
471 }
472 });
473 }
474 return promise;
475 }
476
477 private void shutdownOutputDone(final ChannelFuture shutdownOutputFuture, final ChannelPromise promise) {
478 ChannelFuture shutdownInputFuture = shutdownInput();
479 if (shutdownInputFuture.isDone()) {
480 shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
481 } else {
482 shutdownInputFuture.addListener(new ChannelFutureListener() {
483 @Override
484 public void operationComplete(ChannelFuture shutdownInputFuture) throws Exception {
485 shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
486 }
487 });
488 }
489 }
490
491 private static void shutdownDone(ChannelFuture shutdownOutputFuture,
492 ChannelFuture shutdownInputFuture,
493 ChannelPromise promise) {
494 Throwable shutdownOutputCause = shutdownOutputFuture.cause();
495 Throwable shutdownInputCause = shutdownInputFuture.cause();
496 if (shutdownOutputCause != null) {
497 if (shutdownInputCause != null) {
498 logger.debug("Exception suppressed because a previous exception occurred.",
499 shutdownInputCause);
500 }
501 promise.setFailure(shutdownOutputCause);
502 } else if (shutdownInputCause != null) {
503 promise.setFailure(shutdownInputCause);
504 } else {
505 promise.setSuccess();
506 }
507 }
508
509 class KQueueStreamUnsafe extends AbstractKQueueUnsafe {
510 @Override
511 protected Executor prepareToClose() {
512
513 removeFastOpenData();
514 return super.prepareToClose();
515 }
516
517 @Override
518 void readReady(final KQueueRecvByteAllocatorHandle allocHandle) {
519 final ChannelConfig config = config();
520 if (shouldBreakReadReady(config)) {
521 clearReadFilter0();
522 return;
523 }
524 final ChannelPipeline pipeline = pipeline();
525 final ByteBufAllocator allocator = config.getAllocator();
526 allocHandle.reset(config);
527
528 ByteBuf byteBuf = null;
529 boolean close = false;
530 try {
531 do {
532
533
534 byteBuf = allocHandle.allocate(allocator);
535 allocHandle.lastBytesRead(doReadBytes(byteBuf));
536 if (allocHandle.lastBytesRead() <= 0) {
537
538 byteBuf.release();
539 byteBuf = null;
540 close = allocHandle.lastBytesRead() < 0;
541 if (close) {
542
543 readPending = false;
544 }
545 break;
546 }
547 allocHandle.incMessagesRead(1);
548 readPending = false;
549 pipeline.fireChannelRead(byteBuf);
550 byteBuf = null;
551
552 if (shouldBreakReadReady(config)) {
553
554
555
556
557
558
559
560
561
562
563
564 break;
565 }
566 } while (allocHandle.continueReading());
567
568 allocHandle.readComplete();
569 pipeline.fireChannelReadComplete();
570
571 if (close || allocHandle.isReadEOF()) {
572 shutdownInput(false);
573 }
574 } catch (Throwable t) {
575 handleReadException(pipeline, byteBuf, t, close, allocHandle);
576 } finally {
577 if (shouldStopReading(config)) {
578 clearReadFilter0();
579 }
580 }
581 }
582
583 private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
584 KQueueRecvByteAllocatorHandle allocHandle) {
585 if (byteBuf != null) {
586 if (byteBuf.isReadable()) {
587 readPending = false;
588 pipeline.fireChannelRead(byteBuf);
589 } else {
590 byteBuf.release();
591 }
592 }
593 if (!failConnectPromise(cause)) {
594 allocHandle.readComplete();
595 pipeline.fireChannelReadComplete();
596 pipeline.fireExceptionCaught(cause);
597
598
599
600 if (close ||
601 cause instanceof OutOfMemoryError ||
602 cause instanceof LeakPresenceDetector.AllocationProhibitedException ||
603 cause instanceof IOException) {
604 shutdownInput(false);
605 }
606 }
607 }
608 }
609
610 private final class KQueueSocketWritableByteChannel extends SocketWritableByteChannel {
611 KQueueSocketWritableByteChannel() {
612 super(socket);
613 }
614
615 @Override
616 protected ByteBufAllocator alloc() {
617 return AbstractKQueueStreamChannel.this.alloc();
618 }
619 }
620 }