@Override
 public void ping(PingInfo pingInfo, Promise<PingResultInfo> promise) {
   int pingId = pingIds.getAndAdd(2);
   PingInfoCallback pingInfoCallback = new PingInfoCallback(pingId, promise);
   PingFrame frame = new PingFrame(version, pingId);
   control(null, frame, pingInfo.getTimeout(), pingInfo.getUnit(), pingInfoCallback);
 }
 private void onPing(PingFrame frame) {
   int pingId = frame.getPingId();
   if (pingId % 2 == pingIds.get() % 2) {
     PingResultInfo pingResultInfo = new PingResultInfo(frame.getPingId());
     notifyOnPing(listener, pingResultInfo);
     flush();
   } else {
     control(null, frame, 0, TimeUnit.MILLISECONDS, new Callback.Adapter());
   }
 }
 private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Callback callback) {
   if (goAwaySent.compareAndSet(false, true)) {
     if (!goAwayReceived.get()) {
       GoAwayFrame frame = new GoAwayFrame(version, lastStreamId.get(), sessionStatus.getCode());
       control(null, frame, timeout, unit, callback);
       return;
     }
   }
   complete(callback);
 }
 @Override
 public void rst(RstInfo rstInfo, Callback callback) {
   // SPEC v3, 2.2.2
   if (goAwaySent.get()) {
     complete(callback);
   } else {
     int streamId = rstInfo.getStreamId();
     IStream stream = streams.get(streamId);
     RstStreamFrame frame =
         new RstStreamFrame(version, streamId, rstInfo.getStreamStatus().getCode(version));
     control(stream, frame, rstInfo.getTimeout(), rstInfo.getUnit(), callback);
     if (stream != null) {
       stream.process(frame);
       removeStream(stream);
     }
   }
 }
 @Override
 public void settings(SettingsInfo settingsInfo, Callback callback) {
   SettingsFrame frame =
       new SettingsFrame(version, settingsInfo.getFlags(), settingsInfo.getSettings());
   control(null, frame, settingsInfo.getTimeout(), settingsInfo.getUnit(), callback);
 }