package de.skuzzle.polly.tools.io;
public class IntervalAllocationStrategy implements AllocationStrategy {
protected final Object lock;
private final int intervalLength;
private final int maxBytesPerInterval;
private long lastResetTime;
private long lastAllocationTime;
private int lastAllocation;
private boolean sleep;
private int bytesLeft;
private double speed;
/**
* Creates a new DefaultAllocationStrategy.
*
* @param pMaxBytesPerInterval The maximum of bytes that can be allocated within the
* provided interval
* @param pIntervalLength The above mentioned interval in milliseconds.
*/
public IntervalAllocationStrategy(int pMaxBytesPerInterval, int pIntervalLength) {
this.lock = new Object();
this.intervalLength = pIntervalLength;
this.maxBytesPerInterval = pMaxBytesPerInterval;
this.bytesLeft = maxBytesPerInterval;
this.sleep = true;
this.lastResetTime = System.nanoTime();
this.lastAllocationTime = System.nanoTime();
}
@Override
public void registerConsumer(Object obj) {}
@Override
public void consumerFinished(Object obj) {}
@Override
public double getSpeed() {
synchronized (this.lock) {
return this.speed * 1000000000;
}
}
@Override
public int allocate(Object source, int bytes) {
synchronized (this.lock) {
final long now = System.nanoTime();
final int totallyAllocated = this.maxBytesPerInterval - this.bytesLeft;
final long timePassedSinceAllocation = System.nanoTime() - this.lastAllocationTime;
// reduce amounts of bytes allocated in this run so the currently requested
// amount of bytes are available by the end of the interval
if (this.lastAllocation != 0) {
final double timePassedSinceAllocationMs = timePassedSinceAllocation / 1000000.0;
final double allocationsDurationMs = (double) this.lastAllocation / (double) timePassedSinceAllocationMs;
final int bytesNew = (int) (((now + this.intervalLength - 1.f + totallyAllocated) * allocationsDurationMs) / (allocationsDurationMs * totallyAllocated * this.maxBytesPerInterval));
bytes = Math.min(bytesNew, bytes);
}
final long timePassedSinceReset = System.nanoTime() - this.lastResetTime;
final long timePassedSinceResetMs = timePassedSinceReset / 1000000;
final long timeUntilResetMs = Math.max(0, this.intervalLength - timePassedSinceResetMs);
if (timePassedSinceResetMs > this.intervalLength) {
final double bytesPerInterval = (double) (this.maxBytesPerInterval - this.bytesLeft);
this.speed = timePassedSinceReset == 0 ? 0.f : bytesPerInterval / (double) timePassedSinceReset;
this.bytesLeft = this.maxBytesPerInterval;
this.lastResetTime = System.nanoTime();
}
final int allocated = Math.max(0, Math.min(this.bytesLeft, bytes));
if (this.sleep && allocated == 0) {
try {
// increases the possibility that there are bytes available on the next
// call to allocate
Thread.sleep(timeUntilResetMs);
} catch (InterruptedException e) {
return 0;
}
}
this.bytesLeft -= allocated;
this.lastAllocation = allocated;
this.lastAllocationTime = System.nanoTime();
return allocated;
}
}
@Override
public void close() {
}
}