Author: toad
Date: 2006-01-24 20:54:14 +0000 (Tue, 24 Jan 2006)
New Revision: 7909

Added:
   branches/async-client/src/freenet/support/IntNumberedItem.java
   branches/async-client/src/freenet/support/NumberedItemComparator.java
   branches/async-client/src/freenet/support/RandomGrabArray.java
   branches/async-client/src/freenet/support/RandomGrabArrayItem.java
   branches/async-client/src/freenet/support/RandomGrabArrayWithInt.java
   
branches/async-client/src/freenet/support/SimpleIntNumberedItemComparator.java
   branches/async-client/src/freenet/support/SortedVectorByNumber.java
Modified:
   branches/async-client/src/freenet/client/async/ClientGet.java
   branches/async-client/src/freenet/client/async/ClientPut.java
   branches/async-client/src/freenet/client/async/ClientRequest.java
   branches/async-client/src/freenet/client/async/ClientRequestScheduler.java
   branches/async-client/src/freenet/client/async/SendableRequest.java
   branches/async-client/src/freenet/client/async/SingleBlockInserter.java
   branches/async-client/src/freenet/client/async/SingleFileFetcher.java
   branches/async-client/src/freenet/node/RequestStarter.java
   branches/async-client/src/freenet/support/NumberedRecentItems.java
Log:
Almost implemented ClientRequestScheduler.
Now need to wire it up to RequestStarter etc.

Modified: branches/async-client/src/freenet/client/async/ClientGet.java
===================================================================
--- branches/async-client/src/freenet/client/async/ClientGet.java       
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/ClientGet.java       
2006-01-24 20:54:14 UTC (rev 7909)
@@ -73,5 +73,9 @@
                                currentState.cancel();
                }
        }
+
+       public boolean isFinished() {
+               return finished || cancelled;
+       }

 }

Modified: branches/async-client/src/freenet/client/async/ClientPut.java
===================================================================
--- branches/async-client/src/freenet/client/async/ClientPut.java       
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/ClientPut.java       
2006-01-24 20:54:14 UTC (rev 7909)
@@ -72,4 +72,8 @@
                }
        }

+       public boolean isFinished() {
+               return finished || cancelled;
+       }
+       
 }

Modified: branches/async-client/src/freenet/client/async/ClientRequest.java
===================================================================
--- branches/async-client/src/freenet/client/async/ClientRequest.java   
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/ClientRequest.java   
2006-01-24 20:54:14 UTC (rev 7909)
@@ -27,5 +27,5 @@
                return cancelled;
        }

-
+       public abstract boolean isFinished();
 }

Modified: 
branches/async-client/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- branches/async-client/src/freenet/client/async/ClientRequestScheduler.java  
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/ClientRequestScheduler.java  
2006-01-24 20:54:14 UTC (rev 7909)
@@ -1,5 +1,10 @@
 package freenet.client.async;

+import freenet.crypt.RandomSource;
+import freenet.node.RequestStarter;
+import freenet.support.RandomGrabArrayWithInt;
+import freenet.support.SortedVectorByNumber;
+
 /**
  * Every X seconds, the RequestSender calls the ClientRequestScheduler to
  * ask for a request to start. A request is then started, in its own 
@@ -7,29 +12,87 @@
  */
 public class ClientRequestScheduler {

+       /**
+        * Structure:
+        * array (by priority) -> // one element per possible priority
+        * SortedVectorByNumber (by # retries) -> // contains each current 
#retries
+        * RandomGrabArray // contains each element, allows fast 
fetch-and-drop-a-random-element
+        * 
+        * To speed up fetching, a RGA or SVBN must only exist if it is 
non-empty.
+        */
+       final SortedVectorByNumber[] priorities;
+       // we have one for inserts and one for requests
+       final boolean isInsertScheduler;
+       final RandomSource random;

-       
-       public void register(SendableGet req) {
-               // FIXME
+       ClientRequestScheduler(boolean forInserts, RandomSource random) {
+               this.random = random;
+               this.isInsertScheduler = forInserts;
+               priorities = new 
SortedVectorByNumber[RequestStarter.NUMBER_OF_PRIORITY_CLASSES];
+               for(int i=0;i<priorities.length;i++)
+                       priorities[i] = new SortedVectorByNumber();
        }

-       public void register(SendableInsert req) {
-               // FIXME
+       public synchronized void register(SendableRequest req) {
+               if((!isInsertScheduler) && req instanceof ClientPut)
+                       throw new IllegalArgumentException("Expected a 
ClientPut: "+req);
+               RandomGrabArrayWithInt grabber = 
+                       makeGrabArray(req.getPriorityClass(), 
req.getRetryCount());
+               grabber.add(req);
        }

-       public void remove(SendableRequest sr) {
-               // FIXME
+       private synchronized RandomGrabArrayWithInt makeGrabArray(short 
priorityClass, int retryCount) {
+               SortedVectorByNumber prio = priorities[priorityClass];
+               if(prio == null) {
+                       prio = new SortedVectorByNumber();
+                       priorities[priorityClass] = prio;
+               }
+               RandomGrabArrayWithInt grabber = (RandomGrabArrayWithInt) 
prio.get(retryCount);
+               if(grabber == null) {
+                       grabber = new RandomGrabArrayWithInt(retryCount);
+                       prio.add(grabber);
+               }
+               return grabber;
        }
-       
-       public void update(SendableRequest sr) {
-               // FIXME
+
+       /**
+        * Should not be called often as can be slow if there are many requests 
of the same
+        * priority and retry count. Priority and retry count must be the same 
as they were
+        * when it was added.
+        */
+       public synchronized void remove(SendableRequest req) {
+               // Should not be called often.
+               int prio = req.getPriorityClass();
+               int retryCount = req.getRetryCount();
+               SortedVectorByNumber s = priorities[prio];
+               if(s == null) return;
+               if(s.isEmpty()) return;
+               RandomGrabArrayWithInt grabber = 
+                       (RandomGrabArrayWithInt) s.get(retryCount);
+               if(grabber == null) return;
+               grabber.remove(req);
+               if(grabber.isEmpty()) {
+                       s.remove(retryCount);
+                       if(s.isEmpty())
+                               priorities[prio] = null;
+               }
        }

-       public void startRequest() {
-               // FIXME
+       public synchronized ClientRequest getFirst() {
+               // Priorities start at 0
+               for(int i=0;i<RequestStarter.MINIMUM_PRIORITY_CLASS;i++) {
+                       SortedVectorByNumber s = priorities[i];
+                       if(s == null) continue;
+                       RandomGrabArrayWithInt rga = (RandomGrabArrayWithInt) 
s.getFirst(); // will discard finished items
+                       ClientRequest req = (ClientRequest) rga.removeRandom();
+                       if(rga.isEmpty()) {
+                               s.remove(rga.getNumber());
+                               if(s.isEmpty()) {
+                                       priorities[i] = null;
+                               }
+                       }
+                       return req;
+               }
+               return null;
        }
-       
-       public void startInsert() {
-               // FIXME
-       }
 }

Modified: branches/async-client/src/freenet/client/async/SendableRequest.java
===================================================================
--- branches/async-client/src/freenet/client/async/SendableRequest.java 
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/SendableRequest.java 
2006-01-24 20:54:14 UTC (rev 7909)
@@ -1,10 +1,12 @@
 package freenet.client.async;

+import freenet.support.RandomGrabArrayItem;
+
 /**
  * A low-level request which can be sent immediately. These are registered
  * on the ClientRequestScheduler.
  */
-public interface SendableRequest {
+public interface SendableRequest extends RandomGrabArrayItem {

        public short getPriorityClass();


Modified: 
branches/async-client/src/freenet/client/async/SingleBlockInserter.java
===================================================================
--- branches/async-client/src/freenet/client/async/SingleBlockInserter.java     
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/SingleBlockInserter.java     
2006-01-24 20:54:14 UTC (rev 7909)
@@ -196,4 +196,8 @@
                cb.onFailure(new 
InserterException(InserterException.CANCELLED), this);
        }

+       public boolean isFinished() {
+               return finished;
+       }
+
 }

Modified: branches/async-client/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- branches/async-client/src/freenet/client/async/SingleFileFetcher.java       
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/client/async/SingleFileFetcher.java       
2006-01-24 20:54:14 UTC (rev 7909)
@@ -464,4 +464,8 @@
                cancelled = true;
        }

+       public boolean isFinished() {
+               return cancelled;
+       }
+
 }

Modified: branches/async-client/src/freenet/node/RequestStarter.java
===================================================================
--- branches/async-client/src/freenet/node/RequestStarter.java  2006-01-24 
00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/node/RequestStarter.java  2006-01-24 
20:54:14 UTC (rev 7909)
@@ -33,6 +33,8 @@
        /** Anything less important than prefetch (redundant??) */
        public static final short MINIMUM_PRIORITY_CLASS = 6;

+       public static final short NUMBER_OF_PRIORITY_CLASSES = 
MINIMUM_PRIORITY_CLASS - MAXIMUM_PRIORITY_CLASS;
+       
        // Clients registered
        final Vector clientsByPriority;
        final RequestThrottle throttle;

Added: branches/async-client/src/freenet/support/IntNumberedItem.java
===================================================================
--- branches/async-client/src/freenet/support/IntNumberedItem.java      
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/IntNumberedItem.java      
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,11 @@
+package freenet.support;
+
+/**
+ * An object with a number (as an int).
+ * @see NumberedItem
+ */
+public interface IntNumberedItem {
+
+    int getNumber();
+    
+}

Added: branches/async-client/src/freenet/support/NumberedItemComparator.java
===================================================================
--- branches/async-client/src/freenet/support/NumberedItemComparator.java       
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/NumberedItemComparator.java       
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,59 @@
+package freenet.support;
+
+import java.util.Comparator;
+
+public class NumberedItemComparator implements Comparator {
+
+       public NumberedItemComparator(boolean wrap) {
+               this.wrapAround = wrap;
+       }
+       
+       final boolean wrapAround;
+       
+    public int compare(Object o1, Object o2) {
+        int x = ocompare(o1, o2);
+        Logger.minor(this, "compare("+o1+","+o2+") = "+x);
+        return x;
+    }
+    
+    public int ocompare(Object o1, Object o2) {
+        // Nulls at the end of the list
+        if(o1 == null && o2 == null)
+            return 0; // null == null
+        if(o1 != null && o2 == null)
+            return 1; // anything > null
+        if(o2 != null && o1 == null)
+            return -1;
+        long i1, i2;
+        if(o1 instanceof NumberedItem)
+            i1 = ((NumberedItem)o1).getNumber();
+        else if(o1 instanceof Long)
+            i1 = ((Long)o1).longValue();
+        else throw new ClassCastException(o1.toString());
+        if(o2 instanceof NumberedItem)
+            i2 = ((NumberedItem)o2).getNumber();
+        else if(o2 instanceof Long)
+            i2 = ((Long)o2).longValue();
+        else throw new ClassCastException(o2.toString());
+        if(i1 == i2) return 0;
+        if(!wrapAround) {
+            if(i1 > i2) return 1;
+            else return -1;
+        } else {
+            long firstDistance, secondDistance;
+            if(i1 > i2) {
+                firstDistance = i1 - i2; // smaller => i1 > i2
+                secondDistance = i2 + Long.MAX_VALUE - i1; // smaller => i2 > 
i1
+            } else {
+                secondDistance = i2 - i1; // smaller => i2 > i1
+                firstDistance = i1 + Long.MAX_VALUE - i2; // smaller => i1 > i2
+            }
+            if(Math.abs(firstDistance) < Math.abs(secondDistance)) {
+                return 1; // i1>i2
+            } else //if(Math.abs(secondDistance) < Math.abs(firstDistance)) {
+                return -1; // i2>i1
+            // REDFLAG: base must be odd, so we never get ==
+        }
+    }
+    
+}

Modified: branches/async-client/src/freenet/support/NumberedRecentItems.java
===================================================================
--- branches/async-client/src/freenet/support/NumberedRecentItems.java  
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/NumberedRecentItems.java  
2006-01-24 20:54:14 UTC (rev 7909)
@@ -28,56 +28,7 @@
         items = new NumberedItem[maxSize];
         count = 0;
         wrapAround = wrap;
-        myComparator = 
-            new Comparator() {
-
-            public int compare(Object o1, Object o2) {
-                int x = ocompare(o1, o2);
-                Logger.minor(this, "compare("+o1+","+o2+") = "+x);
-                return x;
-            }
-            
-            public int ocompare(Object o1, Object o2) {
-                // Nulls at the end of the list
-                if(o1 == null && o2 == null)
-                    return 0; // null == null
-                if(o1 != null && o2 == null)
-                    return 1; // anything > null
-                if(o2 != null && o1 == null)
-                    return -1;
-                long i1, i2;
-                if(o1 instanceof NumberedItem)
-                    i1 = ((NumberedItem)o1).getNumber();
-                else if(o1 instanceof Long)
-                    i1 = ((Long)o1).longValue();
-                else throw new ClassCastException(o1.toString());
-                if(o2 instanceof NumberedItem)
-                    i2 = ((NumberedItem)o2).getNumber();
-                else if(o2 instanceof Long)
-                    i2 = ((Long)o2).longValue();
-                else throw new ClassCastException(o2.toString());
-                if(i1 == i2) return 0;
-                if(!wrapAround) {
-                    if(i1 > i2) return 1;
-                    else return -1;
-                } else {
-                    long firstDistance, secondDistance;
-                    if(i1 > i2) {
-                        firstDistance = i1 - i2; // smaller => i1 > i2
-                        secondDistance = i2 + Long.MAX_VALUE - i1; // smaller 
=> i2 > i1
-                    } else {
-                        secondDistance = i2 - i1; // smaller => i2 > i1
-                        firstDistance = i1 + Long.MAX_VALUE - i2; // smaller 
=> i1 > i2
-                    }
-                    if(Math.abs(firstDistance) < Math.abs(secondDistance)) {
-                        return 1; // i1>i2
-                    } else //if(Math.abs(secondDistance) < 
Math.abs(firstDistance)) {
-                        return -1; // i2>i1
-                    // REDFLAG: base must be odd, so we never get ==
-                }
-            }
-            
-        };
+        myComparator = new NumberedItemComparator(wrap);
     }

     public synchronized NumberedItem get(int num) {

Added: branches/async-client/src/freenet/support/RandomGrabArray.java
===================================================================
--- branches/async-client/src/freenet/support/RandomGrabArray.java      
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/RandomGrabArray.java      
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,72 @@
+package freenet.support;
+
+import java.util.HashSet;
+
+import freenet.crypt.RandomSource;
+
+/**
+ * An array which supports very fast remove-and-return-a-random-element.
+ */
+public class RandomGrabArray {
+
+       /** Array of items. Non-null's followed by null's. */
+       private RandomGrabArrayItem[] reqs;
+       /** Index of first null item. */
+       private int index;
+       /** Random source */
+       private RandomSource rand;
+       /** What do we already have? FIXME: Replace with a Bloom filter or 
something (to save 
+        * RAM), or rewrite the whole class as a custom hashset maybe based on 
the classpath 
+        * HashSet. Note that removeRandom() is *the* common operation, so MUST 
BE FAST.
+        */
+       private HashSet contents;
+       private final static int MIN_SIZE = 32;
+       
+       public synchronized void add(RandomGrabArrayItem req) {
+               if(contents.contains(req)) return;
+               if(req.isFinished()) return;
+               contents.add(req);
+               if(index >= reqs.length) {
+                       RandomGrabArray[] r = new 
RandomGrabArray[reqs.length*2];
+                       System.arraycopy(reqs, 0, r, 0, reqs.length);
+               }
+               reqs[index++] = req;
+       }
+       
+       public synchronized RandomGrabArrayItem removeRandom() {
+               while(true) {
+                       if(index == 0) return null;
+                       int i = rand.nextInt(index);
+                       RandomGrabArrayItem ret = reqs[i];
+                       reqs[i] = reqs[--index];
+                       reqs[index] = null;
+                       if(ret != null)
+                               contents.remove(ret);
+                       // Shrink array
+                       if(index < reqs.length / 4 && reqs.length > MIN_SIZE) {
+                               // Shrink array
+                               int newSize = Math.max(index * 2, MIN_SIZE);
+                               RandomGrabArrayItem[] r = new 
RandomGrabArrayItem[newSize];
+                               System.arraycopy(reqs, 0, r, 0, r.length);
+                               reqs = r;
+                       }
+                       if(ret != null && !ret.isFinished()) return ret;
+               }
+       }
+       
+       public synchronized void remove(RandomGrabArrayItem it) {
+               if(!contents.contains(it)) return;
+               contents.remove(it);
+               for(int i=0;i<index;i++) {
+                       if(reqs[i] == it || reqs[i].equals(it)) {
+                               reqs[i] = reqs[--index];
+                               reqs[index] = null;
+                               return;
+                       }
+               }
+       }
+
+       public boolean isEmpty() {
+               return index == 0;
+       }
+}

Added: branches/async-client/src/freenet/support/RandomGrabArrayItem.java
===================================================================
--- branches/async-client/src/freenet/support/RandomGrabArrayItem.java  
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/RandomGrabArrayItem.java  
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,8 @@
+package freenet.support;
+
+public interface RandomGrabArrayItem {
+
+       /** If true, will be automatically removed from the RGA, and not 
returned. */
+       public boolean isFinished();
+       
+}

Added: branches/async-client/src/freenet/support/RandomGrabArrayWithInt.java
===================================================================
--- branches/async-client/src/freenet/support/RandomGrabArrayWithInt.java       
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/RandomGrabArrayWithInt.java       
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,15 @@
+package freenet.support;
+
+public class RandomGrabArrayWithInt extends RandomGrabArray implements 
IntNumberedItem {
+
+       private final int number;
+
+       public RandomGrabArrayWithInt(int no) {
+               number = no;
+       }
+       
+       public int getNumber() {
+               return number;
+       }
+
+}

Added: 
branches/async-client/src/freenet/support/SimpleIntNumberedItemComparator.java
===================================================================
--- 
branches/async-client/src/freenet/support/SimpleIntNumberedItemComparator.java  
    2006-01-24 00:05:09 UTC (rev 7908)
+++ 
branches/async-client/src/freenet/support/SimpleIntNumberedItemComparator.java  
    2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,38 @@
+package freenet.support;
+
+import java.util.Comparator;
+
+public class SimpleIntNumberedItemComparator implements Comparator {
+
+    public int compare(Object o1, Object o2) {
+        int x = ocompare(o1, o2);
+        Logger.minor(this, "compare("+o1+","+o2+") = "+x);
+        return x;
+    }
+    
+    public int ocompare(Object o1, Object o2) {
+        // Nulls at the end of the list
+        if(o1 == null && o2 == null)
+            return 0; // null == null
+        if(o1 != null && o2 == null)
+            return 1; // anything > null
+        if(o2 != null && o1 == null)
+            return -1;
+        long i1, i2;
+        if(o1 instanceof IntNumberedItem)
+            i1 = ((IntNumberedItem)o1).getNumber();
+        else if(o1 instanceof Integer)
+            i1 = ((Integer)o1).intValue();
+        else throw new ClassCastException(o1.toString());
+        if(o2 instanceof IntNumberedItem)
+            i2 = ((IntNumberedItem)o2).getNumber();
+        else if(o2 instanceof Integer)
+            i2 = ((Integer)o2).intValue();
+        else throw new ClassCastException(o2.toString());
+        if(i1 == i2) return 0;
+        if(i1 > i2) return 1;
+        else return -1;
+    }
+    
+
+}

Added: branches/async-client/src/freenet/support/SortedVectorByNumber.java
===================================================================
--- branches/async-client/src/freenet/support/SortedVectorByNumber.java 
2006-01-24 00:05:09 UTC (rev 7908)
+++ branches/async-client/src/freenet/support/SortedVectorByNumber.java 
2006-01-24 20:54:14 UTC (rev 7909)
@@ -0,0 +1,61 @@
+package freenet.support;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Map of an integer to an element, based on a sorted Vector.
+ * Note that we have to shuffle data around, so this is slowish if it gets big.
+ */
+public class SortedVectorByNumber {
+
+       private IntNumberedItem[] data;
+       private int length;
+       private static final Comparator comparator = new 
SimpleIntNumberedItemComparator();
+       
+       public synchronized IntNumberedItem getFirst() {
+               if(length == 0) return null;
+               return data[0];
+       }
+
+       public boolean isEmpty() {
+               return length == 0;
+       }
+
+       public synchronized IntNumberedItem get(int retryCount) {
+               int x = Arrays.binarySearch(data, new Integer(retryCount), 
comparator);
+               if(x >= 0)
+                       return data[x];
+               return null;
+       }
+
+       public void remove(int item) {
+               int x = Arrays.binarySearch(data, new Integer(item), 
comparator);
+               if(x >= 0) {
+                       if(x < length-1)
+                               System.arraycopy(data, x+1, data, x, 
length-x-1);
+                       data[length--] = null;
+               }
+               if(length < 4*data.length) {
+                       IntNumberedItem[] newData = new 
IntNumberedItem[length*2];
+                       System.arraycopy(data, 0, newData, 0, length);
+                       data = newData;
+               }
+       }
+
+       public void add(IntNumberedItem grabber) {
+               int x = Arrays.binarySearch(data, new 
Integer(grabber.getNumber()), comparator);
+               if(x >= 0) throw new IllegalArgumentException(); // already 
exists
+               // insertion point
+               x = -x-1;
+               // Move the data
+               if(length == data.length) {
+                       IntNumberedItem[] newData = new 
IntNumberedItem[length*2];
+                       System.arraycopy(data, 0, newData, 0, data.length);
+               }
+               if(x < length)
+                       System.arraycopy(data, x, data, x+1, length-x);
+               data[x] = grabber;
+       }
+
+}


Reply via email to