Author: toad
Date: 2008-04-02 01:29:15 +0000 (Wed, 02 Apr 2008)
New Revision: 18905

Modified:
   trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
   trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java
   trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
   trunk/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
   trunk/freenet/src/freenet/node/RequestScheduler.java
Log:
Fix critical bug in the cooldown queue: We MUST store the time, the key and 
*the client*, otherwise if there is a collision on the time and key, which 
happens right now in the splitfile code, we will end up queueing the block to 
the cooldown queue and then removing it from it and never reregistering!

Modified: trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java   
2008-04-02 01:08:37 UTC (rev 18904)
+++ trunk/freenet/src/freenet/client/async/BaseSingleFileFetcher.java   
2008-04-02 01:29:15 UTC (rev 18905)
@@ -78,7 +78,7 @@
                                if(cooldownWakeupTime > now)
                                        Logger.error(this, "Already on the 
cooldown queue for "+this, new Exception("error"));
                                else
-                               cooldownWakeupTime = sched.queueCooldown(key);
+                               cooldownWakeupTime = sched.queueCooldown(key, 
this);
                                return true; // We will retry, just not yet. 
See requeueAfterCooldown(Key).
                        } else {
                                schedule();

Modified: trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java  
2008-04-02 01:08:37 UTC (rev 18904)
+++ trunk/freenet/src/freenet/client/async/ClientRequestScheduler.java  
2008-04-02 01:29:15 UTC (rev 18905)
@@ -558,7 +558,7 @@
                                offeredKeys[i].remove(key);
                }
                if(cooldownQueue != null)
-                       cooldownQueue.removeKey(key, 
getter.getCooldownWakeupByKey(key));
+                       cooldownQueue.removeKey(key, getter, 
getter.getCooldownWakeupByKey(key));
        }

        /**
@@ -629,12 +629,12 @@
                if(o instanceof SendableGet) {
                        gets = new SendableGet[] { (SendableGet) o };
                        if(cooldownQueue != null)
-                               cooldownQueue.removeKey(key, 
((SendableGet)o).getCooldownWakeupByKey(key));
+                               cooldownQueue.removeKey(key, (SendableGet)o, 
((SendableGet)o).getCooldownWakeupByKey(key));
                } else {
                        gets = (SendableGet[]) o;
                        if(cooldownQueue != null)
                                for(int i=0;i<gets.length;i++)
-                                       cooldownQueue.removeKey(key, 
gets[i].getCooldownWakeupByKey(key));
+                                       cooldownQueue.removeKey(key, gets[i], 
gets[i].getCooldownWakeupByKey(key));

                }
                if(gets == null) return;
@@ -698,8 +698,8 @@
                }
        }

-       public long queueCooldown(ClientKey key) {
-               return cooldownQueue.add(key.getNodeKey());
+       public long queueCooldown(ClientKey key, SendableGet getter) {
+               return cooldownQueue.add(key.getNodeKey(), getter);
        }

        public void moveKeysFromCooldownQueue() {

Modified: trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
===================================================================
--- trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java    
2008-04-02 01:08:37 UTC (rev 18904)
+++ trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java    
2008-04-02 01:29:15 UTC (rev 18905)
@@ -4,6 +4,7 @@
 package freenet.client.async;

 import freenet.keys.Key;
+import freenet.node.SendableGet;
 import freenet.support.Fields;
 import freenet.support.Logger;

@@ -21,6 +22,8 @@
        private Key[] keys;
        /** times at which keys will be valid again */
        private long[] times;
+       /** clients responsible for the keys */
+       private SendableGet[] clients;
        /** count of keys removed from middle i.e. holes */
        int holes;
        /** location of first (chronologically) key */
@@ -37,6 +40,7 @@
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                keys = new Key[MIN_SIZE];
                times = new long[MIN_SIZE];
+               clients = new SendableGet[MIN_SIZE];
                holes = 0;
                startPtr = 0;
                endPtr = 0;
@@ -46,13 +50,13 @@
        /**
         * Add a key to the end of the queue. Returns the time at which it will 
be valid again.
         */
-       synchronized long add(Key key) {
+       synchronized long add(Key key, SendableGet client) {
                long removeTime = System.currentTimeMillis() + cooldownTime;
                if(removeTime < getLastTime()) {
                        removeTime = getLastTime();
                        Logger.error(this, "CLOCK SKEW DETECTED!!! Attempting 
to compensate, expect things to break!");
                }
-               add(key, removeTime);
+               add(key, client, removeTime);
                return removeTime;
        }

@@ -62,7 +66,7 @@
                return times[times.length-1];
        }

-       private synchronized void add(Key key, long removeTime) {
+       private synchronized void add(Key key, SendableGet client, long 
removeTime) {
                if(holes < 0) Logger.error(this, "holes = "+holes+" !!");
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR)
@@ -75,7 +79,7 @@
                                if(startPtr == 0) {
                                        // No room
                                        expandQueue();
-                                       add(key);
+                                       add(key, client);
                                        return;
                                } else {
                                        // Wrap around
@@ -88,7 +92,7 @@
                        if(logMINOR) Logger.minor(this, "endPtr < startPtr");
                        if(endPtr == startPtr - 1) {
                                expandQueue();
-                               add(key);
+                               add(key, client);
                                return;
                        } else {
                                endPtr++;
@@ -102,6 +106,7 @@
                if(logMINOR) Logger.minor(this, "Added at "+ptr+" 
startPtr="+startPtr+" endPtr="+endPtr);
                keys[ptr] = key;
                times[ptr] = removeTime;
+               clients[ptr] = client;
                return;
        }

@@ -123,6 +128,7 @@
                        Key key = keys[startPtr];
                        if(key == null) {
                                times[startPtr] = 0;
+                               clients[startPtr] = null;
                                startPtr++;
                                holes--;
                                if(startPtr == times.length) startPtr = 0;
@@ -135,6 +141,7 @@
                                }
                                times[startPtr] = 0;
                                keys[startPtr] = null;
+                               clients[startPtr] = null;
                                startPtr++;
                                if(startPtr == times.length) startPtr = 0;
                        }
@@ -146,7 +153,7 @@
        /**
         * @return True if the key was found.
         */
-       synchronized boolean removeKey(Key key, long time) {
+       synchronized boolean removeKey(Key key, SendableGet client, long time) {
                if(time <= 0) return false; // We won't find it.
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(holes < 0) Logger.error(this, "holes = "+holes+" !!");
@@ -166,8 +173,9 @@
                }
                if(logMINOR) Logger.minor(this, "idx = "+idx);
                if(idx < 0) return false;
-               if(keys[idx] == key) {
+               if(keys[idx] == key && clients[idx] == client) {
                        keys[idx] = null;
+                       clients[idx] = null;
                        holes++;
                        if(logMINOR) Logger.minor(this, "Found (exact)");
                        return true;
@@ -176,8 +184,9 @@
                int nidx = idx;
                while(true) {
                        if(times[nidx] != time) break;
-                       if(keys[nidx] == key) {
+                       if(keys[nidx] == key && clients[nidx] == client) {
                                keys[nidx] = null;
+                               clients[nidx] = null;
                                holes++;
                                if(logMINOR) Logger.minor(this, "Found 
(backwards)");
                                return true;
@@ -190,8 +199,9 @@
                // Now try forwards
                while(true) {
                        if(times[nidx] != time) break;
-                       if(keys[nidx] == key) {
+                       if(keys[nidx] == key && clients[nidx] == client) {
                                keys[nidx] = null;
+                               clients[nidx] = null;
                                holes++;
                                if(logMINOR) Logger.minor(this, "Found 
(forwards)");
                                return true;
@@ -218,6 +228,7 @@
                // FIXME reuse the old buffers if it fits
                Key[] newKeys = new Key[newSize];
                long[] newTimes = new long[newSize];
+               SendableGet[] newClients = new SendableGet[newSize];
                // Reset startPtr to 0, and remove holes.
                int x = 0;
                long lastTime = -1;
@@ -226,6 +237,7 @@
                                if(keys[i] == null) continue;
                                newKeys[x] = keys[i];
                                newTimes[x] = times[i];
+                               newClients[x] = clients[i];
                                if(lastTime > times[i])
                                        Logger.error(this, 
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but 
lastTime="+lastTime);
                                lastTime = times[i];
@@ -236,6 +248,7 @@
                                if(keys[i] == null) continue;
                                newKeys[x] = keys[i];
                                newTimes[x] = times[i];
+                               newClients[x] = clients[i];
                                if(lastTime > times[i])
                                        Logger.error(this, 
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but 
lastTime="+lastTime);
                                lastTime = times[i];
@@ -245,6 +258,7 @@
                                if(keys[i] == null) continue;
                                newKeys[x] = keys[i];
                                newTimes[x] = times[i];
+                               newClients[x] = clients[i];
                                if(lastTime > times[i])
                                        Logger.error(this, 
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but 
lastTime="+lastTime);
                                lastTime = times[i];
@@ -258,6 +272,7 @@
                startPtr = 0;
                keys = newKeys;
                times = newTimes;
+               clients = newClients;
                endPtr = x;
        }


Modified: trunk/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SplitFileFetcherSegment.java 
2008-04-02 01:08:37 UTC (rev 18904)
+++ trunk/freenet/src/freenet/client/async/SplitFileFetcherSegment.java 
2008-04-02 01:29:15 UTC (rev 18905)
@@ -394,7 +394,9 @@
                                        if(dataCooldownTimes[blockNo] > now)
                                                Logger.error(this, "Already on 
the cooldown queue! for "+this+" data block no "+blockNo, new 
Exception("error"));
                                        else
-                                       dataCooldownTimes[blockNo] = 
sched.queueCooldown(key);
+                                       // FIXME ideally we'd only register 
once, with the new segment, but the cost is 
+                                       // trivial, and it simplifies locking. 
Reconsider sometime...
+                                       dataCooldownTimes[blockNo] = 
sched.queueCooldown(key, seg);
                                        cooldown = true;
                                }
                        } else {
@@ -407,7 +409,7 @@
                                        if(checkCooldownTimes[checkNo] > now)
                                                Logger.error(this, "Already on 
the cooldown queue! for "+this+" check block no "+blockNo, new 
Exception("error"));
                                        else
-                                       checkCooldownTimes[checkNo] = 
sched.queueCooldown(key);
+                                       checkCooldownTimes[checkNo] = 
sched.queueCooldown(key, seg);
                                        cooldown = true;
                                }
                        }

Modified: trunk/freenet/src/freenet/node/RequestScheduler.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestScheduler.java        2008-04-02 
01:08:37 UTC (rev 18904)
+++ trunk/freenet/src/freenet/node/RequestScheduler.java        2008-04-02 
01:29:15 UTC (rev 18905)
@@ -25,7 +25,7 @@
         * @param key The key to be added.
         * @return The time at which the key will leave the cooldown queue.
         */
-       public long queueCooldown(ClientKey key);
+       public long queueCooldown(ClientKey key, SendableGet getter);

        /**
         * Remove keys from the cooldown queue who have now served their time 
and can be requested 


Reply via email to