1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel;
17
18 import io.netty.buffer.AbstractReferenceCountedByteBuf;
19 import io.netty.buffer.ByteBuf;
20 import io.netty.buffer.ByteBufHolder;
21 import io.netty.buffer.Unpooled;
22 import io.netty.channel.socket.nio.NioSocketChannel;
23 import io.netty.util.Recycler;
24 import io.netty.util.Recycler.EnhancedHandle;
25 import io.netty.util.ReferenceCountUtil;
26 import io.netty.util.concurrent.FastThreadLocal;
27 import io.netty.util.internal.InternalThreadLocalMap;
28 import io.netty.util.internal.ObjectPool.Handle;
29 import io.netty.util.internal.ObjectUtil;
30 import io.netty.util.internal.PromiseNotificationUtil;
31 import io.netty.util.internal.SystemPropertyUtil;
32 import io.netty.util.internal.logging.InternalLogger;
33 import io.netty.util.internal.logging.InternalLoggerFactory;
34
35 import java.nio.ByteBuffer;
36 import java.nio.channels.ClosedChannelException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
41 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
42
43 import static java.lang.Math.min;
44
45
46
47
48
49
50
51
52
53
54
55
56 public final class ChannelOutboundBuffer {
57
58
59
60
61
62
63
64 static final int CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD =
65 SystemPropertyUtil.getInt("io.netty.transport.outboundBufferEntrySizeOverhead", 96);
66
67 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelOutboundBuffer.class);
68
69 private static final FastThreadLocal<ByteBuffer[]> NIO_BUFFERS = new FastThreadLocal<ByteBuffer[]>() {
70 @Override
71 protected ByteBuffer[] initialValue() throws Exception {
72 return new ByteBuffer[1024];
73 }
74 };
75
76 private final Channel channel;
77
78
79
80
81 private Entry flushedEntry;
82
83 private Entry unflushedEntry;
84
85 private Entry tailEntry;
86
87 private int flushed;
88
89 private int nioBufferCount;
90 private long nioBufferSize;
91
92 private boolean inFail;
93
94 private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =
95 AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");
96
97 @SuppressWarnings("UnusedDeclaration")
98 private volatile long totalPendingSize;
99
100 private static final AtomicIntegerFieldUpdater<ChannelOutboundBuffer> UNWRITABLE_UPDATER =
101 AtomicIntegerFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "unwritable");
102
103 @SuppressWarnings("UnusedDeclaration")
104 private volatile int unwritable;
105
106 private volatile Runnable fireChannelWritabilityChangedTask;
107
108 ChannelOutboundBuffer(AbstractChannel channel) {
109 this.channel = channel;
110 }
111
112
113
114
115
116 public void addMessage(Object msg, int size, ChannelPromise promise) {
117 Entry entry = Entry.newInstance(msg, size, total(msg), promise);
118 if (tailEntry == null) {
119 flushedEntry = null;
120 } else {
121 Entry tail = tailEntry;
122 tail.next = entry;
123 }
124 tailEntry = entry;
125 if (unflushedEntry == null) {
126 unflushedEntry = entry;
127 }
128
129
130
131
132
133 if (msg instanceof AbstractReferenceCountedByteBuf) {
134 ((AbstractReferenceCountedByteBuf) msg).touch();
135 } else {
136 ReferenceCountUtil.touch(msg);
137 }
138
139
140
141 incrementPendingOutboundBytes(entry.pendingSize, false);
142 }
143
144
145
146
147
148 public void addFlush() {
149
150
151
152
153 Entry entry = unflushedEntry;
154 if (entry != null) {
155 if (flushedEntry == null) {
156
157 flushedEntry = entry;
158 }
159 long cancelledBytes = 0;
160 List<Object> cancelledMsgs = null;
161 do {
162 flushed ++;
163 if (!entry.promise.setUncancellable()) {
164
165
166 if (cancelledMsgs == null) {
167 cancelledMsgs = new ArrayList<Object>(2);
168 }
169 cancelledMsgs.add(entry.msg);
170 cancelledBytes += entry.cancel();
171 }
172 entry = entry.next;
173 } while (entry != null);
174
175
176 unflushedEntry = null;
177
178 if (cancelledMsgs != null) {
179 for (int i = 0; i < cancelledMsgs.size(); i++) {
180 ReferenceCountUtil.safeRelease(cancelledMsgs.get(i));
181 }
182
183
184
185 decrementPendingOutboundBytes(cancelledBytes, false, true);
186 }
187 }
188 }
189
190
191
192
193
194 void incrementPendingOutboundBytes(long size) {
195 incrementPendingOutboundBytes(size, true);
196 }
197
198 private void incrementPendingOutboundBytes(long size, boolean invokeLater) {
199 if (size == 0) {
200 return;
201 }
202
203 long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, size);
204 if (newWriteBufferSize > channel.config().getWriteBufferHighWaterMark()) {
205 setUnwritable(invokeLater);
206 }
207 }
208
209
210
211
212
213 void decrementPendingOutboundBytes(long size) {
214 decrementPendingOutboundBytes(size, true, true);
215 }
216
217 private void decrementPendingOutboundBytes(long size, boolean invokeLater, boolean notifyWritability) {
218 if (size == 0) {
219 return;
220 }
221
222 long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, -size);
223 if (notifyWritability && newWriteBufferSize < channel.config().getWriteBufferLowWaterMark()) {
224 setWritable(invokeLater);
225 }
226 }
227
228 private static long total(Object msg) {
229 if (msg instanceof ByteBuf) {
230 return ((ByteBuf) msg).readableBytes();
231 }
232 if (msg instanceof FileRegion) {
233 return ((FileRegion) msg).count();
234 }
235 if (msg instanceof ByteBufHolder) {
236 return ((ByteBufHolder) msg).content().readableBytes();
237 }
238 return -1;
239 }
240
241
242
243
244 public Object current() {
245 Entry entry = flushedEntry;
246 if (entry == null) {
247 return null;
248 }
249
250 return entry.msg;
251 }
252
253
254
255
256
257 public long currentProgress() {
258 Entry entry = flushedEntry;
259 if (entry == null) {
260 return 0;
261 }
262 return entry.progress;
263 }
264
265
266
267
268 public void progress(long amount) {
269 Entry e = flushedEntry;
270 assert e != null;
271 ChannelPromise p = e.promise;
272 long progress = e.progress + amount;
273 e.progress = progress;
274 assert p != null;
275 final Class<?> promiseClass = p.getClass();
276
277 if (promiseClass == VoidChannelPromise.class || promiseClass == DefaultChannelPromise.class) {
278 return;
279 }
280
281 if (p instanceof DefaultChannelProgressivePromise) {
282 ((DefaultChannelProgressivePromise) p).tryProgress(progress, e.total);
283 } else if (p instanceof ChannelProgressivePromise) {
284 ((ChannelProgressivePromise) p).tryProgress(progress, e.total);
285 }
286 }
287
288
289
290
291
292
293 public boolean remove() {
294 Entry e = flushedEntry;
295 if (e == null) {
296 clearNioBuffers();
297 return false;
298 }
299 Object msg = e.msg;
300
301 ChannelPromise promise = e.promise;
302 int size = e.pendingSize;
303
304 removeEntry(e);
305
306
307 if (!e.cancelled) {
308
309
310 if (msg instanceof AbstractReferenceCountedByteBuf) {
311 try {
312
313 ((AbstractReferenceCountedByteBuf) msg).release();
314 } catch (Throwable t) {
315 logger.warn("Failed to release a ByteBuf: {}", msg, t);
316 }
317 } else {
318 ReferenceCountUtil.safeRelease(msg);
319 }
320 safeSuccess(promise);
321 decrementPendingOutboundBytes(size, false, true);
322 }
323
324
325 e.unguardedRecycle();
326
327 return true;
328 }
329
330
331
332
333
334
335 public boolean remove(Throwable cause) {
336 return remove0(cause, true);
337 }
338
339 private boolean remove0(Throwable cause, boolean notifyWritability) {
340 Entry e = flushedEntry;
341 if (e == null) {
342 clearNioBuffers();
343 return false;
344 }
345 Object msg = e.msg;
346
347 ChannelPromise promise = e.promise;
348 int size = e.pendingSize;
349
350 removeEntry(e);
351
352 if (!e.cancelled) {
353
354 ReferenceCountUtil.safeRelease(msg);
355
356 safeFail(promise, cause);
357 decrementPendingOutboundBytes(size, false, notifyWritability);
358 }
359
360
361 e.unguardedRecycle();
362
363 return true;
364 }
365
366 private void removeEntry(Entry e) {
367 if (-- flushed == 0) {
368
369 flushedEntry = null;
370 if (e == tailEntry) {
371 tailEntry = null;
372 unflushedEntry = null;
373 }
374 } else {
375 flushedEntry = e.next;
376 }
377 }
378
379
380
381
382
383 public void removeBytes(long writtenBytes) {
384 for (;;) {
385 Object msg = current();
386 if (!(msg instanceof ByteBuf)) {
387 assert writtenBytes == 0;
388 break;
389 }
390
391 final ByteBuf buf = (ByteBuf) msg;
392 final int readerIndex = buf.readerIndex();
393 final int readableBytes = buf.writerIndex() - readerIndex;
394
395 if (readableBytes <= writtenBytes) {
396 if (writtenBytes != 0) {
397 progress(readableBytes);
398 writtenBytes -= readableBytes;
399 }
400 remove();
401 } else {
402 if (writtenBytes != 0) {
403 buf.readerIndex(readerIndex + (int) writtenBytes);
404 progress(writtenBytes);
405 }
406 break;
407 }
408 }
409 clearNioBuffers();
410 }
411
412
413
414 private void clearNioBuffers() {
415 int count = nioBufferCount;
416 if (count > 0) {
417 nioBufferCount = 0;
418 Arrays.fill(NIO_BUFFERS.get(), 0, count, null);
419 }
420 }
421
422
423
424
425
426
427
428
429
430
431
432 public ByteBuffer[] nioBuffers() {
433 return nioBuffers(Integer.MAX_VALUE, Integer.MAX_VALUE);
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 public ByteBuffer[] nioBuffers(int maxCount, long maxBytes) {
451 assert maxCount > 0;
452 assert maxBytes > 0;
453 long nioBufferSize = 0;
454 int nioBufferCount = 0;
455 final InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
456 ByteBuffer[] nioBuffers = NIO_BUFFERS.get(threadLocalMap);
457 Entry entry = flushedEntry;
458 while (isFlushedEntry(entry) && entry.msg instanceof ByteBuf) {
459 if (!entry.cancelled) {
460 ByteBuf buf = (ByteBuf) entry.msg;
461 final int readerIndex = buf.readerIndex();
462 final int readableBytes = buf.writerIndex() - readerIndex;
463
464 if (readableBytes > 0) {
465 if (maxBytes - readableBytes < nioBufferSize && nioBufferCount != 0) {
466
467
468
469
470
471
472
473
474
475
476
477 break;
478 }
479 nioBufferSize += readableBytes;
480 int count = entry.count;
481 if (count == -1) {
482
483 entry.count = count = buf.nioBufferCount();
484 }
485 int neededSpace = min(maxCount, nioBufferCount + count);
486 if (neededSpace > nioBuffers.length) {
487 nioBuffers = expandNioBufferArray(nioBuffers, neededSpace, nioBufferCount);
488 NIO_BUFFERS.set(threadLocalMap, nioBuffers);
489 }
490 if (count == 1) {
491 ByteBuffer nioBuf = entry.buf;
492 if (nioBuf == null) {
493
494
495 entry.buf = nioBuf = buf.internalNioBuffer(readerIndex, readableBytes);
496 }
497 nioBuffers[nioBufferCount++] = nioBuf;
498 } else {
499
500
501 nioBufferCount = nioBuffers(entry, buf, nioBuffers, nioBufferCount, maxCount);
502 }
503 if (nioBufferCount >= maxCount) {
504 break;
505 }
506 }
507 }
508 entry = entry.next;
509 }
510 this.nioBufferCount = nioBufferCount;
511 this.nioBufferSize = nioBufferSize;
512
513 return nioBuffers;
514 }
515
516 private static int nioBuffers(Entry entry, ByteBuf buf, ByteBuffer[] nioBuffers, int nioBufferCount, int maxCount) {
517 ByteBuffer[] nioBufs = entry.bufs;
518 if (nioBufs == null) {
519
520
521 entry.bufs = nioBufs = buf.nioBuffers();
522 }
523 for (int i = 0; i < nioBufs.length && nioBufferCount < maxCount; ++i) {
524 ByteBuffer nioBuf = nioBufs[i];
525 if (nioBuf == null) {
526 break;
527 } else if (!nioBuf.hasRemaining()) {
528 continue;
529 }
530 nioBuffers[nioBufferCount++] = nioBuf;
531 }
532 return nioBufferCount;
533 }
534
535 private static ByteBuffer[] expandNioBufferArray(ByteBuffer[] array, int neededSpace, int size) {
536 int newCapacity = array.length;
537 do {
538
539
540 newCapacity <<= 1;
541
542 if (newCapacity < 0) {
543 throw new IllegalStateException();
544 }
545
546 } while (neededSpace > newCapacity);
547
548 ByteBuffer[] newArray = new ByteBuffer[newCapacity];
549 System.arraycopy(array, 0, newArray, 0, size);
550
551 return newArray;
552 }
553
554
555
556
557
558
559 public int nioBufferCount() {
560 return nioBufferCount;
561 }
562
563
564
565
566
567
568 public long nioBufferSize() {
569 return nioBufferSize;
570 }
571
572
573
574
575
576
577
578 public boolean isWritable() {
579 return unwritable == 0;
580 }
581
582
583
584
585
586 public boolean getUserDefinedWritability(int index) {
587 return (unwritable & writabilityMask(index)) == 0;
588 }
589
590
591
592
593 public void setUserDefinedWritability(int index, boolean writable) {
594 if (writable) {
595 setUserDefinedWritability(index);
596 } else {
597 clearUserDefinedWritability(index);
598 }
599 }
600
601 private void setUserDefinedWritability(int index) {
602 final int mask = ~writabilityMask(index);
603 for (;;) {
604 final int oldValue = unwritable;
605 final int newValue = oldValue & mask;
606 if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
607 if (oldValue != 0 && newValue == 0) {
608 fireChannelWritabilityChanged(true);
609 }
610 break;
611 }
612 }
613 }
614
615 private void clearUserDefinedWritability(int index) {
616 final int mask = writabilityMask(index);
617 for (;;) {
618 final int oldValue = unwritable;
619 final int newValue = oldValue | mask;
620 if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
621 if (oldValue == 0 && newValue != 0) {
622 fireChannelWritabilityChanged(true);
623 }
624 break;
625 }
626 }
627 }
628
629 private static int writabilityMask(int index) {
630 if (index < 1 || index > 31) {
631 throw new IllegalArgumentException("index: " + index + " (expected: 1~31)");
632 }
633 return 1 << index;
634 }
635
636 private void setWritable(boolean invokeLater) {
637 for (;;) {
638 final int oldValue = unwritable;
639 final int newValue = oldValue & ~1;
640 if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
641 if (oldValue != 0 && newValue == 0) {
642 fireChannelWritabilityChanged(invokeLater);
643 }
644 break;
645 }
646 }
647 }
648
649 private void setUnwritable(boolean invokeLater) {
650 for (;;) {
651 final int oldValue = unwritable;
652 final int newValue = oldValue | 1;
653 if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
654 if (oldValue == 0) {
655 fireChannelWritabilityChanged(invokeLater);
656 }
657 break;
658 }
659 }
660 }
661
662 private void fireChannelWritabilityChanged(boolean invokeLater) {
663 final ChannelPipeline pipeline = channel.pipeline();
664 if (invokeLater) {
665 Runnable task = fireChannelWritabilityChangedTask;
666 if (task == null) {
667 fireChannelWritabilityChangedTask = task = new Runnable() {
668 @Override
669 public void run() {
670 pipeline.fireChannelWritabilityChanged();
671 }
672 };
673 }
674 channel.eventLoop().execute(task);
675 } else {
676 pipeline.fireChannelWritabilityChanged();
677 }
678 }
679
680
681
682
683 public int size() {
684 return flushed;
685 }
686
687
688
689
690
691 public boolean isEmpty() {
692 return flushed == 0;
693 }
694
695 void failFlushed(Throwable cause, boolean notify) {
696
697
698
699
700
701 if (inFail) {
702 return;
703 }
704
705 try {
706 inFail = true;
707 for (;;) {
708 if (!remove0(cause, notify)) {
709 break;
710 }
711 }
712 } finally {
713 inFail = false;
714 }
715 }
716
717 void close(final Throwable cause, final boolean allowChannelOpen) {
718 if (inFail) {
719 channel.eventLoop().execute(new Runnable() {
720 @Override
721 public void run() {
722 close(cause, allowChannelOpen);
723 }
724 });
725 return;
726 }
727
728 inFail = true;
729
730 if (!allowChannelOpen && channel.isOpen()) {
731 throw new IllegalStateException("close() must be invoked after the channel is closed.");
732 }
733
734 if (!isEmpty()) {
735 throw new IllegalStateException("close() must be invoked after all flushed writes are handled.");
736 }
737
738
739 try {
740 Entry e = unflushedEntry;
741 while (e != null) {
742
743 int size = e.pendingSize;
744 TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, -size);
745
746 if (!e.cancelled) {
747 ReferenceCountUtil.safeRelease(e.msg);
748 safeFail(e.promise, cause);
749 }
750 e = e.unguardedRecycleAndGetNext();
751 }
752 } finally {
753 inFail = false;
754 }
755 clearNioBuffers();
756 }
757
758 void close(ClosedChannelException cause) {
759 close(cause, false);
760 }
761
762 private static void safeSuccess(ChannelPromise promise) {
763
764
765 PromiseNotificationUtil.trySuccess(promise, null, promise instanceof VoidChannelPromise ? null : logger);
766 }
767
768 private static void safeFail(ChannelPromise promise, Throwable cause) {
769
770
771 PromiseNotificationUtil.tryFailure(promise, cause, promise instanceof VoidChannelPromise ? null : logger);
772 }
773
774 @Deprecated
775 public void recycle() {
776
777 }
778
779 public long totalPendingWriteBytes() {
780 return totalPendingSize;
781 }
782
783
784
785
786
787 public long bytesBeforeUnwritable() {
788
789 long bytes = channel.config().getWriteBufferHighWaterMark() - totalPendingSize + 1;
790
791
792
793 return bytes > 0 && isWritable() ? bytes : 0;
794 }
795
796
797
798
799
800 public long bytesBeforeWritable() {
801
802 long bytes = totalPendingSize - channel.config().getWriteBufferLowWaterMark() + 1;
803
804
805
806 return bytes <= 0 || isWritable() ? 0 : bytes;
807 }
808
809
810
811
812
813
814 public void forEachFlushedMessage(MessageProcessor processor) throws Exception {
815 ObjectUtil.checkNotNull(processor, "processor");
816
817 Entry entry = flushedEntry;
818 if (entry == null) {
819 return;
820 }
821
822 do {
823 if (!entry.cancelled) {
824 if (!processor.processMessage(entry.msg)) {
825 return;
826 }
827 }
828 entry = entry.next;
829 } while (isFlushedEntry(entry));
830 }
831
832 private boolean isFlushedEntry(Entry e) {
833 return e != null && e != unflushedEntry;
834 }
835
836 public interface MessageProcessor {
837
838
839
840
841 boolean processMessage(Object msg) throws Exception;
842 }
843
844 static final class Entry {
845 private static final Recycler<Entry> RECYCLER = new Recycler<Entry>() {
846 @Override
847 protected Entry newObject(Handle<Entry> handle) {
848 return new Entry(handle);
849 }
850 };
851
852 private final EnhancedHandle<Entry> handle;
853 Entry next;
854 Object msg;
855 ByteBuffer[] bufs;
856 ByteBuffer buf;
857 ChannelPromise promise;
858 long progress;
859 long total;
860 int pendingSize;
861 int count = -1;
862 boolean cancelled;
863
864 private Entry(Handle<Entry> handle) {
865 this.handle = (EnhancedHandle<Entry>) handle;
866 }
867
868 static Entry newInstance(Object msg, int size, long total, ChannelPromise promise) {
869 Entry entry = RECYCLER.get();
870 entry.msg = msg;
871 entry.pendingSize = size + CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD;
872 entry.total = total;
873 entry.promise = promise;
874 return entry;
875 }
876
877 int cancel() {
878 if (!cancelled) {
879 cancelled = true;
880 int pSize = pendingSize;
881
882
883 msg = Unpooled.EMPTY_BUFFER;
884
885 pendingSize = 0;
886 total = 0;
887 progress = 0;
888 bufs = null;
889 buf = null;
890 return pSize;
891 }
892 return 0;
893 }
894
895 void unguardedRecycle() {
896 next = null;
897 bufs = null;
898 buf = null;
899 msg = null;
900 promise = null;
901 progress = 0;
902 total = 0;
903 pendingSize = 0;
904 count = -1;
905 cancelled = false;
906 handle.unguardedRecycle(this);
907 }
908
909 Entry unguardedRecycleAndGetNext() {
910 Entry next = this.next;
911 unguardedRecycle();
912 return next;
913 }
914 }
915 }