code: public synchronized ListenableFuture<ChangeRequestsSnapshot<T>> getRequestsSince(final Counter counter)
    {
      final CustomSettableFuture<T> future = new CustomSettableFuture<>(waitingFutures);
  
      if (counter.counter < 0) {
        future.setException(new IAE("counter[%s] must be >= 0", counter));
        return future;
      }
  
      Counter lastCounter = getLastCounter();
  
      if (counter.counter == lastCounter.counter) {
        if (!counter.matches(lastCounter)) {
          ChangeRequestsSnapshot<T> reset = ChangeRequestsSnapshot.fail(
              StringUtils.format("counter[%s] failed to match with [%s]", counter, lastCounter)
          );
          future.set(reset);
        } else {
          synchronized (waitingFutures) {
            waitingFutures.put(future, counter);
          }
        }
      } else {
        try {
          future.set(getRequestsSinceWithoutWait(counter));
        }
        catch (Exception ex) {
          future.setException(ex);
        }
      }
  
      return future;
    }
docs: Returns a Future that, on completion, returns list of segment updates and associated counter.
  If there are no update since given counter then Future completion waits till an updates is provided.
  
  If counter is older than max number of changes maintained then {@link ChangeRequestsSnapshot} is returned
  with {@link ChangeRequestsSnapshot#resetCounter} set to True.
  
  If there were no updates to provide immediately then a future is created and returned to caller. This future
  is added to the "waitingFutures" list and all the futures in the list get resolved as soon as a segment
  update is provided.