/**
* Copyright 2014 Ricardo Padilha
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dsys.snio.impl.limit;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import net.dsys.commons.api.lang.BinaryUnit;
import net.dsys.snio.api.limit.RateLimiter;
import org.isomorphism.util.TokenBucket;
import org.isomorphism.util.TokenBuckets;
/**
* @author Ricardo Padilha
*/
final class TokenBucketLimiter implements RateLimiter {
/**
* Refill rate constants.
* These translate to "refill each 100ms with a 10th of the refill rate."
*/
private static final int REFILL_FACTOR = 10;
private static final int REFILL_INTERVAL = 100;
private static final TimeUnit REFILL_UNIT = TimeUnit.MILLISECONDS;
private static final int BASE_INTERVAL = 1;
private static final TimeUnit BASE_UNIT = TimeUnit.SECONDS;
private TokenBucket send;
private TokenBucket recv;
TokenBucketLimiter(@Nonnegative final long value, @Nonnull final BinaryUnit unit) {
setRate(value, unit);
}
private static TokenBucket createBucket(@Nonnegative final long bits) {
// Instead of refilling once per second, we refill 10 times per second.
// It makes for a smoother bandwidth curve under very low rates,
// e.g., less than 100 kbps.
final long bytes = bits / Byte.SIZE;
final long refillRate = bytes / REFILL_FACTOR;
// if the division caused an underflow, revert to base units.
if (refillRate < 1) {
return TokenBuckets.builder()
.withCapacity(bytes)
.withYieldingSleepStrategy()
.withFixedIntervalRefillStrategy(bytes, BASE_INTERVAL, BASE_UNIT)
.build();
}
return TokenBuckets.builder()
.withCapacity(bytes)
.withYieldingSleepStrategy()
.withFixedIntervalRefillStrategy(refillRate, REFILL_INTERVAL, REFILL_UNIT)
.build();
}
/**
* {@inheritDoc}
*/
@Override
public void setRate(final long value, final BinaryUnit unit) {
if (value < 1) {
throw new IllegalArgumentException("value < 1");
}
final long bits = unit.toBits(value);
this.send = createBucket(bits);
this.recv = createBucket(bits);
}
/**
* {@inheritDoc}
*/
@Override
public void send(final long bytes) {
if (bytes > 0) {
send.consume(bytes);
}
}
/**
* {@inheritDoc}
*/
@Override
public void receive(final long bytes) {
if (bytes > 0) {
recv.consume(bytes);
}
}
}