View Javadoc
1   /*
2    * Copyright 2013 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;
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   * (Transport implementors only) an internal data structure used by {@link AbstractChannel} to store its pending
48   * outbound write requests.
49   * <p>
50   * All methods must be called by a transport implementation from an I/O thread, except the following ones:
51   * <ul>
52   * <li>{@link #isWritable()}</li>
53   * <li>{@link #getUserDefinedWritability(int)} and {@link #setUserDefinedWritability(int, boolean)}</li>
54   * </ul>
55   * </p>
56   */
57  public final class ChannelOutboundBuffer {
58      // Assuming a 64-bit JVM:
59      //  - 16 bytes object header
60      //  - 6 reference fields
61      //  - 2 long fields
62      //  - 2 int fields
63      //  - 1 boolean field
64      //  - padding
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      // Entry(flushedEntry) --> ... Entry(unflushedEntry) --> ... Entry(tailEntry)
80      //
81      // The Entry that is the first in the linked-list structure that was flushed
82      private Entry flushedEntry;
83      // The Entry which is the first unflushed in the linked-list structure
84      private Entry unflushedEntry;
85      // The Entry which represents the tail of the buffer
86      private Entry tailEntry;
87      // The number of flushed entries that are not written yet
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      * Add given message to this {@link ChannelOutboundBuffer}. The given {@link ChannelPromise} will be notified once
115      * the message was written.
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         // Touch the message to make it easier to debug buffer leaks.
131 
132         // this save both checking against the ReferenceCounted interface
133         // and makes better use of virtual calls vs interface ones
134         if (msg instanceof AbstractReferenceCountedByteBuf) {
135             ((AbstractReferenceCountedByteBuf) msg).touch();
136         } else {
137             ReferenceCountUtil.touch(msg);
138         }
139 
140         // increment pending bytes after adding message to the unflushed arrays.
141         // See https://github.com/netty/netty/issues/1619
142         incrementPendingOutboundBytes(entry.pendingSize, false);
143     }
144 
145     /**
146      * Add a flush to this {@link ChannelOutboundBuffer}. This means all previous added messages are marked as flushed
147      * and so you will be able to handle them.
148      */
149     public void addFlush() {
150         // There is no need to process all entries if there was already a flush before and no new messages
151         // where added in the meantime.
152         //
153         // See https://github.com/netty/netty/issues/2577
154         Entry entry = unflushedEntry;
155         if (entry != null) {
156             if (flushedEntry == null) {
157                 // there is no flushedEntry yet, so start with the entry
158                 flushedEntry = entry;
159             }
160             long cancelledBytes = 0;
161             List<Object> cancelledMsgs = null;
162             do {
163                 flushed ++;
164                 if (!entry.promise.setUncancellable()) {
165                     // Was cancelled so make sure we free up memory. The message is released only once this buffer is
166                     // consistent again, as releasing it may run arbitrary user code.
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             // All flushed so reset unflushedEntry
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                 // Notify about the freed bytes only now. This may fire channelWritabilityChanged(...) synchronously,
184                 // and a handler may write, flush or close the channel from there, so this buffer must be consistent
185                 // first.
186                 decrementPendingOutboundBytes(cancelledBytes, false, true);
187             }
188         }
189     }
190 
191     /**
192      * Increment the pending bytes which will be written at some point.
193      * This method is thread-safe!
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      * Decrement the pending bytes which will be written at some point.
212      * This method is thread-safe!
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      * Return the current message to write or {@code null} if nothing was flushed before and so is ready to be written.
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      * Return the current message flush progress.
256      * @return {@code 0} if nothing was flushed before for the current message or there is no current message
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      * Notify the {@link ChannelPromise} of the current message about writing progress.
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         // fast-path to save O(n) ChannelProgressivePromise's type check on OpenJDK
278         if (promiseClass == VoidChannelPromise.class || promiseClass == DefaultChannelPromise.class) {
279             return;
280         }
281         // this is going to save from type pollution due to https://bugs.openjdk.org/browse/JDK-8180450
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      * Will remove the current message, mark its {@link ChannelPromise} as success and return {@code true}. If no
291      * flushed message exists at the time this method is called it will return {@code false} to signal that no more
292      * messages are ready to be handled.
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         // only release message, notify and decrement if it was not canceled before.
308         if (!e.cancelled) {
309             // this save both checking against the ReferenceCounted interface
310             // and makes better use of virtual calls vs interface ones
311             if (msg instanceof AbstractReferenceCountedByteBuf) {
312                 try {
313                     // release now as it is flushed.
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         // recycle the entry
326         e.unguardedRecycle();
327 
328         return true;
329     }
330 
331     /**
332      * Will remove the current message, mark its {@link ChannelPromise} as failure using the given {@link Throwable}
333      * and return {@code true}. If no   flushed message exists at the time this method is called it will return
334      * {@code false} to signal that no more messages are ready to be handled.
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             // only release message, fail and decrement if it was not canceled before.
355             ReferenceCountUtil.safeRelease(msg);
356 
357             safeFail(promise, cause);
358             decrementPendingOutboundBytes(size, false, notifyWritability);
359         }
360 
361         // recycle the entry
362         e.unguardedRecycle();
363 
364         return true;
365     }
366 
367     private void removeEntry(Entry e) {
368         if (-- flushed == 0) {
369             // processed everything
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      * Removes the fully written entries and update the reader index of the partially written entry.
382      * This operation assumes all messages in this buffer is {@link ByteBuf}.
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 { // readableBytes > writtenBytes
403                 if (writtenBytes != 0) {
404                     buf.readerIndex(readerIndex + (int) writtenBytes);
405                     progress(writtenBytes);
406                 }
407                 break;
408             }
409         }
410         clearNioBuffers();
411     }
412 
413     // Clear all ByteBuffer from the array so these can be GC'ed.
414     // See https://github.com/netty/netty/issues/3837
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      * Returns an array of direct NIO buffers if the currently pending messages are made of {@link ByteBuf} only.
425      * {@link #nioBufferCount()} and {@link #nioBufferSize()} will return the number of NIO buffers in the returned
426      * array and the total number of readable bytes of the NIO buffers respectively.
427      * <p>
428      * Note that the returned array is reused and thus should not escape
429      * {@link AbstractChannel#doWrite(ChannelOutboundBuffer)}.
430      * Refer to {@link NioSocketChannel#doWrite(ChannelOutboundBuffer)} for an example.
431      * </p>
432      */
433     public ByteBuffer[] nioBuffers() {
434         return nioBuffers(Integer.MAX_VALUE, Integer.MAX_VALUE);
435     }
436 
437     /**
438      * Returns an array of direct NIO buffers if the currently pending messages are made of {@link ByteBuf} only.
439      * {@link #nioBufferCount()} and {@link #nioBufferSize()} will return the number of NIO buffers in the returned
440      * array and the total number of readable bytes of the NIO buffers respectively.
441      * <p>
442      * Note that the returned array is reused and thus should not escape
443      * {@link AbstractChannel#doWrite(ChannelOutboundBuffer)}.
444      * Refer to {@link NioSocketChannel#doWrite(ChannelOutboundBuffer)} for an example.
445      * </p>
446      * @param maxCount The maximum amount of buffers that will be added to the return value.
447      * @param maxBytes A hint toward the maximum number of bytes to include as part of the return value. Note that this
448      *                 value maybe exceeded because we make a best effort to include at least 1 {@link ByteBuffer}
449      *                 in the return value to ensure write progress is made.
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                         // If the nioBufferSize + readableBytes will overflow maxBytes, and there is at least one entry
468                         // we stop populate the ByteBuffer array. This is done for 2 reasons:
469                         // 1. bsd/osx don't allow to write more bytes then Integer.MAX_VALUE with one writev(...) call
470                         // and so will return 'EINVAL', which will raise an IOException. On Linux it may work depending
471                         // on the architecture and kernel but to be safe we also enforce the limit here.
472                         // 2. There is no sense in putting more data in the array than is likely to be accepted by the
473                         // OS.
474                         //
475                         // See also:
476                         // - https://www.freebsd.org/cgi/man.cgi?query=write&sektion=2
477                         // - https://linux.die.net//man/2/writev
478                         break;
479                     }
480                     nioBufferSize += readableBytes;
481                     int count = entry.count;
482                     if (count == -1) {
483                         //noinspection ConstantValueVariableUse
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                             // cache ByteBuffer as it may need to create a new ByteBuffer instance if its a
495                             // derived buffer
496                             entry.buf = nioBuf = buf.internalNioBuffer(readerIndex, readableBytes);
497                         }
498                         nioBuffers[nioBufferCount++] = nioBuf;
499                     } else {
500                         // The code exists in an extra method to ensure the method is not too big to inline as this
501                         // branch is not very likely to get hit very frequently.
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             // cached ByteBuffers as they may be expensive to create in terms
521             // of Object allocation
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             // double capacity until it is big enough
540             // See https://github.com/netty/netty/issues/1890
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      * Returns the number of {@link ByteBuffer} that can be written out of the {@link ByteBuffer} array that was
557      * obtained via {@link #nioBuffers()}. This method <strong>MUST</strong> be called after {@link #nioBuffers()}
558      * was called.
559      */
560     public int nioBufferCount() {
561         return nioBufferCount;
562     }
563 
564     /**
565      * Returns the number of bytes that can be written out of the {@link ByteBuffer} array that was
566      * obtained via {@link #nioBuffers()}. This method <strong>MUST</strong> be called after {@link #nioBuffers()}
567      * was called.
568      */
569     public long nioBufferSize() {
570         return nioBufferSize;
571     }
572 
573     /**
574      * Returns {@code true} if and only if {@linkplain #totalPendingWriteBytes() the total number of pending bytes} did
575      * not exceed the write watermark of the {@link Channel} and
576      * no {@linkplain #setUserDefinedWritability(int, boolean) user-defined writability flag} has been set to
577      * {@code false}.
578      */
579     public boolean isWritable() {
580         return unwritable == 0;
581     }
582 
583     /**
584      * Returns {@code true} if and only if the user-defined writability flag at the specified index is set to
585      * {@code true}.
586      */
587     public boolean getUserDefinedWritability(int index) {
588         return (unwritable & writabilityMask(index)) == 0;
589     }
590 
591     /**
592      * Sets a user-defined writability flag at the specified index.
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      * Returns the number of flushed messages in this {@link ChannelOutboundBuffer}.
683      */
684     public int size() {
685         return flushed;
686     }
687 
688     /**
689      * Returns {@code true} if there are flushed messages in this {@link ChannelOutboundBuffer} or {@code false}
690      * otherwise.
691      */
692     public boolean isEmpty() {
693         return flushed == 0;
694     }
695 
696     void failFlushed(Throwable cause, boolean notify) {
697         // Make sure that this method does not reenter.  A listener added to the current promise can be notified by the
698         // current thread in the tryFailure() call of the loop below, and the listener can trigger another fail() call
699         // indirectly (usually by closing the channel.)
700         //
701         // See https://github.com/netty/netty/issues/1501
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         // Release all unflushed messages.
740         try {
741             Entry e = unflushedEntry;
742             while (e != null) {
743                 // Just decrease; do not trigger any events via decrementPendingOutboundBytes()
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         // Only log if the given promise is not of type VoidChannelPromise as trySuccess(...) is expected to return
765         // false.
766         PromiseNotificationUtil.trySuccess(promise, null, promise instanceof VoidChannelPromise ? null : logger);
767     }
768 
769     private static void safeFail(ChannelPromise promise, Throwable cause) {
770         // Only log if the given promise is not of type VoidChannelPromise as tryFailure(...) is expected to return
771         // false.
772         PromiseNotificationUtil.tryFailure(promise, cause, promise instanceof VoidChannelPromise ? null : logger);
773     }
774 
775     @Deprecated
776     public void recycle() {
777         // NOOP
778     }
779 
780     public long totalPendingWriteBytes() {
781         return totalPendingSize;
782     }
783 
784     /**
785      * Get how many bytes can be written until {@link #isWritable()} returns {@code false}.
786      * This quantity will always be non-negative. If {@link #isWritable()} is {@code false} then 0.
787      */
788     public long bytesBeforeUnwritable() {
789         // +1 because writability doesn't change until the threshold is crossed (not equal to).
790         long bytes = channel.config().getWriteBufferHighWaterMark() - totalPendingSize + 1;
791         // If bytes is negative we know we are not writable, but if bytes is non-negative we have to check writability.
792         // Note that totalPendingSize and isWritable() use different volatile variables that are not synchronized
793         // together. totalPendingSize will be updated before isWritable().
794         return bytes > 0 && isWritable() ? bytes : 0;
795     }
796 
797     /**
798      * Get how many bytes must be drained from the underlying buffer until {@link #isWritable()} returns {@code true}.
799      * This quantity will always be non-negative. If {@link #isWritable()} is {@code true} then 0.
800      */
801     public long bytesBeforeWritable() {
802         // +1 because writability doesn't change until the threshold is crossed (not equal to).
803         long bytes = totalPendingSize - channel.config().getWriteBufferLowWaterMark() + 1;
804         // If bytes is negative we know we are writable, but if bytes is non-negative we have to check writability.
805         // Note that totalPendingSize and isWritable() use different volatile variables that are not synchronized
806         // together. totalPendingSize will be updated before isWritable().
807         return bytes <= 0 || isWritable() ? 0 : bytes;
808     }
809 
810     /**
811      * Call {@link MessageProcessor#processMessage(Object)} for each flushed message
812      * in this {@link ChannelOutboundBuffer} until {@link MessageProcessor#processMessage(Object)}
813      * returns {@code false} or there are no more flushed messages to process.
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          * Will be called for each flushed message until it either there are no more flushed messages or this
840          * method returns {@code false}.
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                 // replace the message with an empty buffer, the caller is responsible for releasing it
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 }