@Override
 public void data(
     IStream stream, DataInfo dataInfo, long timeout, TimeUnit unit, Callback callback) {
   LOG.debug("Queuing {} on {}", dataInfo, stream);
   DataFrameBytes frameBytes = new DataFrameBytes(stream, callback, dataInfo);
   if (timeout > 0) frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);
   append(frameBytes);
   flush();
 }
  private void generateAndEnqueueControlFrame(
      IStream stream, ControlFrame frame, long timeout, TimeUnit unit, Callback callback) {
    try {
      // Synchronization is necessary, since we may have concurrent replies
      // and those needs to be generated and enqueued atomically in order
      // to maintain a correct compression context
      synchronized (this) {
        ByteBuffer buffer = generator.control(frame);
        LOG.debug("Queuing {} on {}", frame, stream);
        ControlFrameBytes frameBytes = new ControlFrameBytes(stream, callback, frame, buffer);
        if (timeout > 0) frameBytes.task = scheduler.schedule(frameBytes, timeout, unit);

        // Special handling for PING frames, they must be sent as soon as possible
        if (ControlFrameType.PING == frame.getType()) prepend(frameBytes);
        else append(frameBytes);
      }
    } catch (Exception x) {
      notifyCallbackFailed(callback, x);
    }
  }
 @Override
 public void shutdown() {
   FrameBytes frameBytes = new CloseFrameBytes();
   append(frameBytes);
   flush();
 }