Author: toad
Date: 2008-04-18 13:43:11 +0000 (Fri, 18 Apr 2008)
New Revision: 19391

Modified:
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/support/DoubleTokenBucket.java
   trunk/freenet/src/freenet/support/TokenBucket.java
Log:
Rewrite DoubleTokenBucket as per bug #2279. Hopefully bandwidth limiting will 
work a bit better now.

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2008-04-18 12:54:36 UTC (rev 
19390)
+++ trunk/freenet/src/freenet/node/Node.java    2008-04-18 13:43:11 UTC (rev 
19391)
@@ -885,7 +885,7 @@
                                                synchronized(Node.this) {
                                                        outputBandwidthLimit = 
obwLimit;
                                                }
-                                               
outputThrottle.changeNanosAndBucketSizes((1000L * 1000L * 1000L) / obwLimit, 
obwLimit/2, (obwLimit * 2) / 5);
+                                               
outputThrottle.changeNanosAndBucketSize((1000L * 1000L * 1000L) / obwLimit, 
obwLimit/2);
                                                
nodeStats.setOutputLimit(obwLimit);
                                        }
                });
@@ -898,7 +898,7 @@
                // Add them at a rate determined by the obwLimit.
                // Maximum forced bytes 80%, in other words, 20% of the 
bandwidth is reserved for 
                // block transfers, so we will use that 20% for block transfers 
even if more than 80% of the limit is used for non-limited data (resends etc).
-               outputThrottle = new DoubleTokenBucket(obwLimit/2, 
(1000L*1000L*1000L) / obwLimit, obwLimit, (obwLimit * 2) / 5);
+               outputThrottle = new DoubleTokenBucket(obwLimit/2, 
(1000L*1000L*1000L) / obwLimit, obwLimit, 0.8);

                nodeConfig.register("inputBandwidthLimit", "-1", sortOrder++, 
false, true, "Node.inBWLimit", "Node.inBWLimitLong",      new IntCallback() {
                                        public int get() {

Modified: trunk/freenet/src/freenet/support/DoubleTokenBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/DoubleTokenBucket.java    2008-04-18 
12:54:36 UTC (rev 19390)
+++ trunk/freenet/src/freenet/support/DoubleTokenBucket.java    2008-04-18 
13:43:11 UTC (rev 19391)
@@ -1,101 +1,50 @@
 package freenet.support;

 /**
- * A TokenBucket variant which provides the following constraint:
- * Forced token grabs may not use more than M tokens out of the total N.
- * 
- * In other words:
- * A classic token bucket keeps a counter, "current", which must not exceed 
the max, to
- * which we add one token every few millis.
- * 
- * We also keep another counter, unblockableCount, with a separate maximum, 
- * unblockableMax. Whenever a token is added to current by the clock, one is 
removed 
- * from unblockableCount (but only if it is >0). Whenever an unblockable 
request to
- * remove tokens comes in, after we update unblockableCount, we calculate how 
many 
- * tokens we can add to unblockableCount without exceeding the maximum, and 
only allow
- * that many to be removed from counter.
+ * A TokenBucket where forceGrab() may only use up to some proportion of the 
total limit. Beyond that,
+ * we ignore it. So the last X% may be used by blocking grabs, even if the 
forceGrab() traffic is over
+ * the limit. This is implemented by using a secondary TokenBucket to track 
what is allowed.
  */
 public class DoubleTokenBucket extends TokenBucket {

-       private long maxForced;
-       private long curForced;
        private static boolean logMINOR;
+       private final TokenBucket grabbedBytesLimiter;
+       private double forceGrabLimit;

-       /**
-        * Create a DoubleTokenBucket.
-        * @param max The maximum size of the bucket, in tokens.
-        * @param nanosPerTick The number of nanoseconds between ticks.
-        * 
-        */
-       public DoubleTokenBucket(long max, long nanosPerTick, long 
initialValue, long maxForced) {
+       public DoubleTokenBucket(long max, long nanosPerTick, long 
initialValue, double forceGrabLimit) {
                super(max, nanosPerTick, initialValue);
+               if(forceGrabLimit > 1.0) throw new IllegalArgumentException();
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
-               if(logMINOR)
-                       Logger.minor(this, "Max: "+max+" nanosPerTick: 
"+nanosPerTick+" initialValue: "+initialValue+" maxForced: "+maxForced);
-               this.maxForced = maxForced;
-               this.curForced = 0;
+               grabbedBytesLimiter = new TokenBucket((long)(max * 
forceGrabLimit), (long)(nanosPerTick * forceGrabLimit), (long)(initialValue * 
forceGrabLimit));
        }

-       // instantGrab is unchanged.
-       
-       // Major changes to forceGrab ! This is where it happens.
-       
        public synchronized void forceGrab(long tokens) {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
-               addTokens();
-               if(tokens <= 0) {
-                       Logger.error(this, "forceGrab("+tokens+") - negative 
value!!", new Exception("error"));
-                       return;
+               long maxTokens = grabbedBytesLimiter.partialInstantGrab(tokens);
+               if(maxTokens < tokens) {
+                       if(logMINOR) Logger.minor(this, "Limiting forceGrab of 
"+tokens+" to "+maxTokens);
                }
-               long thisMax = maxForced - curForced;
-               if(tokens > thisMax) {
-                       if(logMINOR) Logger.minor(this, "Limiting force-grab to 
"+thisMax+" tokens was "+tokens);
-                       tokens = thisMax;
-               }
-               curForced += tokens;
-               current -= tokens;
-               if(curForced > maxForced) {
-                       curForced = maxForced;
-               }
-               if(logMINOR) Logger.minor(this, "Force-Grabbed "+tokens+" 
current="+current+" forced="+curForced);
+               super.forceGrab(maxTokens);
        }
-       
-       // blockingGrab is unchanged
-       
-       public synchronized void changeSizeOfBuckets(long newMax, long 
newMaxForced) {
-               changeBucketSize(newMax);
-               this.maxForced = newMaxForced;
-               if(curForced > maxForced) curForced = maxForced;
+
+       /**
+        * Change the number of nanos per tick.
+        * @param nanosPerTick The new number of nanos per tick.
+        */
+       public synchronized void changeNanosPerTick(long nanosPerTick) {
+               super.changeNanosPerTick(nanosPerTick);
+               grabbedBytesLimiter.changeNanosPerTick((long)(nanosPerTick * 
forceGrabLimit));
        }

-       public synchronized void changeNanosAndBucketSizes(long nanos, long 
newMax, long newMaxForced) {
-               // FIXME maybe should be combined
-               changeSizeOfBuckets(newMax, newMaxForced);
-               changeNanosPerTick(nanos);
+       public synchronized void changeBucketSize(long newMax) {
+               super.changeBucketSize(newMax);
+               grabbedBytesLimiter.changeBucketSize((long)(newMax * 
forceGrabLimit));
        }

-       public synchronized void addTokensNoClip() {
-               long add = tokensToAdd();
-               current += add;
-               curForced -= add;
-               if(curForced < 0) curForced = 0;
-               timeLastTick += add * nanosPerTick;
-               if(logMINOR) Logger.minor(this, "Added "+add+" tokens 
current="+current+" forced="+curForced);
+       public synchronized void changeNanosAndBucketSize(long nanosPerTick, 
long newMax) {
+               super.changeNanosAndBucketSize(nanosPerTick, newMax);
+               
grabbedBytesLimiter.changeNanosAndBucketSize((long)(nanosPerTick * 
forceGrabLimit),
+                               (long)(newMax * forceGrabLimit));
        }
-
-       public synchronized void addTokens() {
-               if(logMINOR) Logger.minor(this, "current="+current+" 
forced="+curForced);
-               addTokensNoClip();
-               if(curForced > maxForced) curForced = maxForced;
-               if(current > max) current = max;
-       }

-       public synchronized long getSize() {
-               return max;
-       }
-
-       protected synchronized long offset() {
-               return curForced;
-       }
-       
 }

Modified: trunk/freenet/src/freenet/support/TokenBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/TokenBucket.java  2008-04-18 12:54:36 UTC 
(rev 19390)
+++ trunk/freenet/src/freenet/support/TokenBucket.java  2008-04-18 13:43:11 UTC 
(rev 19391)
@@ -54,6 +54,28 @@
        }

        /**
+        * Either grab a bunch of tokens, or don't. Never block.
+        * @param tokens The number of tokens to grab.
+        * @return True if we could acquire the tokens.
+        */
+       public synchronized long partialInstantGrab(long tokens) {
+               if(logMINOR)
+                       Logger.minor(this, "instant grab: "+tokens+" 
current="+current+" max="+max);
+               addTokens();
+               if(logMINOR)
+                       Logger.minor(this, "instant grab: "+tokens+" 
current="+current+" max="+max);
+               if(current > tokens) {
+                       current -= tokens;
+                       if(current > max) current = max;
+                       return tokens;
+               } else {
+                       tokens = current;
+                       current = 0;
+                       return tokens;
+               }
+       }
+       
+       /**
         * Remove tokens, without blocking, even if it causes the balance to go 
negative.
         * @param tokens The number of tokens to remove.
         */


Reply via email to