@Override
  protected void doWrite(ChannelOutboundBuffer in) throws Exception {
    for (; ; ) {
      final int msgCount = in.size();

      if (msgCount == 0) {
        // Wrote all messages.
        clearEpollOut();
        break;
      }

      // Do gathering write if:
      // * the outbound buffer contains more than one messages and
      // * they are all buffers rather than a file region.
      if (msgCount > 1) {
        if (PlatformDependent.hasUnsafe()) {
          // this means we can cast to EpollChannelOutboundBuffer and write the AdressEntry
          // directly.
          EpollChannelOutboundBuffer epollIn = (EpollChannelOutboundBuffer) in;
          // Ensure the pending writes are made of memoryaddresses only.
          AddressEntry[] addresses = epollIn.memoryAddresses();
          if (addresses != null) {
            writeBytesMultiple(epollIn, msgCount, addresses);

            // We do not break the loop here even if the outbound buffer was flushed completely,
            // because a user might have triggered another write and flush when we notify his or her
            // listeners.
            continue;
          }
        } else {
          NioSocketChannelOutboundBuffer nioIn = (NioSocketChannelOutboundBuffer) in;
          // Ensure the pending writes are made of memoryaddresses only.
          ByteBuffer[] buffers = nioIn.nioBuffers();
          if (buffers != null) {
            writeBytesMultiple(nioIn, msgCount, buffers);

            // We do not break the loop here even if the outbound buffer was flushed completely,
            // because a user might have triggered another write and flush when we notify his or her
            // listeners.
            continue;
          }
        }
      }

      // The outbound buffer contains only one message or it contains a file region.
      Object msg = in.current();
      if (msg instanceof ByteBuf) {
        ByteBuf buf = (ByteBuf) msg;
        int readableBytes = buf.readableBytes();
        if (readableBytes == 0) {
          in.remove();
          continue;
        }

        int expected = buf.readableBytes();
        int localFlushedAmount = doWriteBytes(buf, expected);
        in.progress(localFlushedAmount);
        if (localFlushedAmount < expected) {
          setEpollOut();
          break;
        }
        if (!buf.isReadable()) {
          in.remove();
        }

      } else if (msg instanceof DefaultFileRegion) {
        DefaultFileRegion region = (DefaultFileRegion) msg;

        long expected = region.count() - region.position();
        long localFlushedAmount = doWriteFileRegion(region, expected);
        in.progress(localFlushedAmount);

        if (localFlushedAmount < expected) {
          setEpollOut();
          break;
        }

        if (region.transfered() >= region.count()) {
          in.remove();
        }
      } else {
        throw new UnsupportedOperationException(
            "unsupported message type: " + StringUtil.simpleClassName(msg));
      }
    }
  }
 /**
  * Write a {@link DefaultFileRegion}
  *
  * @param region the {@link DefaultFileRegion} from which the bytes should be written
  * @return amount the amount of written bytes
  */
 private long doWriteFileRegion(DefaultFileRegion region, long count) throws Exception {
   return Native.sendfile(fd, region, region.transfered(), count);
 }