@Override
  protected boolean consumeOrAwaitImpl(long tokensToConsume, long waitIfBusyTimeLimit)
      throws InterruptedException {
    Bandwidth[] bandwidths = configuration.getBandwidths();
    boolean isWaitingLimited = waitIfBusyTimeLimit > 0;

    final long methodStartTime = configuration.getTimeMeter().currentTime();
    long currentTime = methodStartTime;
    long methodDuration = 0;
    boolean isFirstCycle = true;

    BucketState previousState = stateReference.get();
    BucketState newState = previousState.clone();

    while (true) {
      if (isFirstCycle) {
        isFirstCycle = false;
      } else {
        currentTime = configuration.getTimeMeter().currentTime();
        methodDuration = currentTime - methodStartTime;
        if (isWaitingLimited && methodDuration >= waitIfBusyTimeLimit) {
          return false;
        }
        previousState = stateReference.get();
        newState.copyState(previousState);
      }

      newState.refill(bandwidths, currentTime);
      long timeToCloseDeficit =
          newState.delayAfterWillBePossibleToConsume(bandwidths, currentTime, tokensToConsume);
      if (timeToCloseDeficit == Long.MAX_VALUE) {
        return false;
      }
      if (timeToCloseDeficit == 0) {
        newState.consume(bandwidths, tokensToConsume);
        if (stateReference.compareAndSet(previousState, newState)) {
          return true;
        } else {
          continue;
        }
      }

      if (isWaitingLimited) {
        long sleepingTimeLimit = waitIfBusyTimeLimit - methodDuration;
        if (timeToCloseDeficit >= sleepingTimeLimit) {
          return false;
        }
      }
      configuration.getTimeMeter().sleep(timeToCloseDeficit);
    }
  }
  @Override
  protected boolean tryConsumeImpl(long tokensToConsume) {
    BucketState previousState = stateReference.get();
    BucketState newState = previousState.clone();
    Bandwidth[] bandwidths = configuration.getBandwidths();
    long currentTime = configuration.getTimeMeter().currentTime();

    while (true) {
      newState.refill(bandwidths, currentTime);
      long availableToConsume = newState.getAvailableTokens(bandwidths);
      if (tokensToConsume > availableToConsume) {
        return false;
      }
      newState.consume(bandwidths, tokensToConsume);
      if (stateReference.compareAndSet(previousState, newState)) {
        return true;
      } else {
        previousState = stateReference.get();
        newState.copyState(previousState);
      }
    }
  }
  @Override
  protected long consumeAsMuchAsPossibleImpl(long limit) {
    BucketState previousState = stateReference.get();
    BucketState newState = previousState.clone();
    Bandwidth[] bandwidths = configuration.getBandwidths();
    long currentTime = configuration.getTimeMeter().currentTime();

    while (true) {
      newState.refill(bandwidths, currentTime);
      long availableToConsume = newState.getAvailableTokens(bandwidths);
      long toConsume = Math.min(limit, availableToConsume);
      if (toConsume == 0) {
        return 0;
      }
      newState.consume(bandwidths, toConsume);
      if (stateReference.compareAndSet(previousState, newState)) {
        return toConsume;
      } else {
        previousState = stateReference.get();
        newState.copyState(previousState);
      }
    }
  }