public void close() {
    if (closed) {
      return;
    }
    closed = true;
    if (!blocked.isDone()) {
      blocked.set(null);
    }
    if (!canAddTsBlock.isDone()) {
      canAddTsBlock.set(null);
    }
    if (blockedOnMemory != null) {
      bufferRetainedSizeInBytes -= localMemoryManager.getQueryPool().tryCancel(blockedOnMemory);
    }
    queue.clear();
    if (bufferRetainedSizeInBytes > 0L) {
      localMemoryManager
          .getQueryPool()
          .free(
              localFragmentInstanceId.getQueryId(),
              fullFragmentInstanceId,
              localPlanNodeId,
              bufferRetainedSizeInBytes);
      bufferRetainedSizeInBytes = 0;
    }
    if (sinkChannel != null) {
      // attention: LocalSinkChannel of this SharedTsBlockQueue could be null when we close
      // LocalSourceHandle(with limit clause it's possible) before constructing the corresponding
      // LocalSinkChannel.
      // If this close method is invoked by LocalSourceHandle, listener of LocalSourceHandle will
      // remove the LocalSourceHandle from the map of MppDataExchangeManager and later when
      // LocalSinkChannel is initialized, it will construct a new SharedTsBlockQueue.
      // It is still safe that we let the LocalSourceHandle close successfully in this case. Because
      // the QueryTerminator will do the final cleaning logic.
      sinkChannel.close();
    }
  }
Destroy the queue and complete the future. Should only be called in normal case
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueue.java