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);
    }
  }