@Override
 public void onNext(final Object w) {
   if (w == null) {
     throw Exceptions.argumentIsNullException();
   }
   if (subscription == null) {
     throw Exceptions.failWithCancel();
   }
   try {
     ChannelFuture cf = doOnWrite(w, ctx);
     lastWrite = cf;
     if (cf != null && log.isDebugEnabled()) {
       cf.addListener(
           new ChannelFutureListener() {
             @Override
             public void operationComplete(ChannelFuture future) throws Exception {
               if (!future.isSuccess()) {
                 log.error("write error :" + w, future.cause());
                 if (ByteBuf.class.isAssignableFrom(w.getClass())) {
                   ((ByteBuf) w).resetReaderIndex();
                 }
               }
             }
           });
     }
   } catch (Throwable t) {
     log.error("Write error for " + w, t);
     onError(t);
   }
 }
    @Override
    public void next(Object value) {
      if (value == null) {
        fail(new NullPointerException("value is null"));
        return;
      }
      if (done) {
        Exceptions.onNextDropped(value);
        return;
      }
      if (caughtUp && actual != null) {
        try {
          actual.onNext(value);
        } finally {
          ch.read();
          ReferenceCountUtil.release(value);
        }

      } else {
        Queue<Object> q = queue;
        if (q == null) {
          q = QueueSupplier.unbounded().get();
          queue = q;
        }
        q.offer(value);
        if (drain()) {
          caughtUp = true;
        }
      }
    }
 @Override
 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
   try {
     inboundEmitter.complete();
     super.channelInactive(ctx);
   } catch (Throwable err) {
     Exceptions.throwIfFatal(err);
     inboundEmitter.fail(err);
   }
 }
 @Override
 public void onNext(Object w) {
   if (w == null) {
     throw Exceptions.argumentIsNullException();
   }
   if (subscription == null) {
     throw Exceptions.failWithCancel();
   }
   try {
     ChannelFuture cf = doOnWrite(w, ctx);
     if (cf != null) {
       cf.addListener(writeListener);
     }
     ctx.flush();
   } catch (Throwable t) {
     log.error("Write error for " + w, t);
     onError(t);
     throw Exceptions.failWithCancel();
   }
 }
 @Override
 public void onError(Throwable t) {
   if (t == null) {
     throw Exceptions.argumentIsNullException();
   }
   if (subscription == null) {
     throw new IllegalStateException("already flushed", t);
   }
   log.error("Write error", t);
   subscription = null;
   ctx.channel().closeFuture().removeListener(this);
   doOnTerminate(ctx, null, promise, t);
 }
 @SuppressWarnings("unchecked")
 protected final void doRead(Object msg) {
   if (msg == null) {
     return;
   }
   try {
     if (msg == Unpooled.EMPTY_BUFFER || msg instanceof EmptyByteBuf) {
       return;
     }
     inboundEmitter.next(msg);
   } catch (Throwable err) {
     Exceptions.throwIfFatal(err);
     inboundEmitter.fail(err);
   }
 }
 @Override
 public void fail(Throwable error) {
   if (error == null) {
     error = new NullPointerException("error is null");
   }
   if (isCancelled() || done) {
     Exceptions.onErrorDropped(error);
     return;
   }
   done = true;
   if (caughtUp && actual != null) {
     actual.onError(error);
   } else {
     this.error = error;
     done = true;
     drain();
   }
 }
 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable err) throws Exception {
   Exceptions.throwIfFatal(err);
   inboundEmitter.fail(err);
 }