@Override
  public void flush() {
    FrameBytes frameBytes = null;
    ByteBuffer buffer = null;
    boolean failFrameBytes = false;
    synchronized (queue) {
      if (flushing || queue.isEmpty()) return;

      Set<IStream> stalledStreams = null;
      for (int i = 0; i < queue.size(); ++i) {
        frameBytes = queue.get(i);

        IStream stream = frameBytes.getStream();
        if (stream != null && stalledStreams != null && stalledStreams.contains(stream)) continue;

        buffer = frameBytes.getByteBuffer();
        if (buffer != null) {
          queue.remove(i);
          if (stream != null && stream.isReset() && !(frameBytes instanceof ControlFrameBytes))
            failFrameBytes = true;
          break;
        }

        if (stalledStreams == null) stalledStreams = new HashSet<>();
        if (stream != null) stalledStreams.add(stream);

        LOG.debug("Flush stalled for {}, {} frame(s) in queue", frameBytes, queue.size());
      }

      if (buffer == null) return;

      if (!failFrameBytes) {
        flushing = true;
        LOG.debug("Flushing {}, {} frame(s) in queue", frameBytes, queue.size());
      }
    }
    if (failFrameBytes) {
      frameBytes.fail(
          new StreamException(
              frameBytes.getStream().getId(),
              StreamStatus.INVALID_STREAM,
              "Stream: " + frameBytes.getStream() + " is reset!"));
    } else {
      write(buffer, frameBytes);
    }
  }
 @Override
 public int compareTo(FrameBytes that) {
   // FrameBytes may have or not have a related stream (for example, PING do not have a related
   // stream)
   // FrameBytes without related streams have higher priority
   IStream thisStream = getStream();
   IStream thatStream = that.getStream();
   if (thisStream == null) return thatStream == null ? 0 : -1;
   if (thatStream == null) return 1;
   // If this.stream.priority > that.stream.priority => this.stream has less priority than
   // that.stream
   return thatStream.getPriority() - thisStream.getPriority();
 }