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