package com.aelitis.azureus.core.peermanager.control.impl; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.disk.DiskManager; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.SystemTime; import com.aelitis.azureus.core.peermanager.control.SpeedTokenDispenser; import com.aelitis.azureus.core.util.FeatureAvailability; public class SpeedTokenDispenserPrioritised implements SpeedTokenDispenser { // crude TBF implementation private int rateKiB; { COConfigurationManager.addAndFireParameterListeners(new String[] { "Max Download Speed KBs", "Use Request Limiting" }, new ParameterListener() { public void parameterChanged(String parameterName) { rateKiB = COConfigurationManager.getIntParameter("Max Download Speed KBs"); if (!COConfigurationManager.getBooleanParameter("Use Request Limiting") || !FeatureAvailability.isRequestLimitingEnabled()) rateKiB = 0; // sanity check if ( rateKiB < 0 ){ rateKiB = 0; } threshold = Math.max(BUCKET_THRESHOLD_FACTOR*rateKiB, BUCKET_THRESHOLD_LOWER_BOUND); lastTime = currentTime - 1; // shortest possible delta refill(); // cap buffer to threshold in case something accumulated } }); } private long threshold; private long bucket = 0; private long lastTime = SystemTime.getCurrentTime(); private long currentTime; public void update(long newTime) { currentTime = newTime; } // allow at least 2 outstanding requests private static final int BUCKET_THRESHOLD_LOWER_BOUND = 2 * DiskManager.BLOCK_SIZE; // time (in seconds) at max speed until the buffer is empty: too low = latency issues; too high = overshooting for too long private static final int BUCKET_RESPONSE_TIME = 1; // n KiB buffer per 1KiB/s speed, that should be roughly n seconds max response time private static final int BUCKET_THRESHOLD_FACTOR = 1024 * BUCKET_RESPONSE_TIME; public void refill() { if (lastTime == currentTime || rateKiB == 0) return; if ( lastTime > currentTime ){ lastTime = currentTime; return; } if ( bucket < 0 ){ Debug.out( "Bucket is more than empty! - " + bucket ); bucket = 0; } long delta = currentTime - lastTime; lastTime = currentTime; // upcast to long since we might exceed int-max when rate and delta are // large enough; then downcast again... long tickDelta = ( rateKiB * 1024L * delta) / 1000; //System.out.println("threshold:" + threshold + " update: " + bucket + " time delta:" + delta); bucket += tickDelta; if (bucket > threshold) bucket = threshold; } public int dispense(int numberOfChunks, int chunkSize) { if (rateKiB == 0) return numberOfChunks; if (chunkSize > bucket) return 0; if (chunkSize * numberOfChunks <= bucket) { bucket -= chunkSize * numberOfChunks; return numberOfChunks; } int availableChunks = (int)( bucket / chunkSize ); bucket -= chunkSize * availableChunks; return availableChunks; } public void returnUnusedChunks(int unused, int chunkSize) { bucket += unused * chunkSize; } public int peek(int chunkSize) { if (rateKiB != 0) return (int)( bucket / chunkSize ); else return Integer.MAX_VALUE; } }