Author: toad
Date: 2008-07-04 14:28:59 +0000 (Fri, 04 Jul 2008)
New Revision: 20993

Added:
   branches/db4o/freenet/src/freenet/client/async/PendingKeyItem.java
Modified:
   
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerBase.java
   
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java
   
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerNonPersistent.java
   branches/db4o/freenet/src/freenet/node/Node.java
Log:
Implement pendingKeys differently for transient vs persistent.
For transient, keep the existing Map-based code.
For persistent, use PendingKeyItem's and queries.
Hopefully this is a speedup.

Modified: 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerBase.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerBase.java  
    2008-07-04 13:58:46 UTC (rev 20992)
+++ 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerBase.java  
    2008-07-04 14:28:59 UTC (rev 20993)
@@ -41,11 +41,6 @@
        final boolean isInsertScheduler;
        final boolean isSSKScheduler;

-       /** All pending gets by key. Used to automatically satisfy pending 
requests when either the key is fetched by
-        * an overlapping request, or it is fetched by a request from another 
node. Operations on this are synchronized on
-        * itself. */
-       protected final Map /* <Key, SendableGet[]> */ pendingKeys;
-       
        /**
         * Structure:
         * array (by priority) -> // one element per possible priority
@@ -60,202 +55,15 @@

        abstract boolean persistent();

-       protected ClientRequestSchedulerBase(boolean forInserts, boolean 
forSSKs, Map pendingKeys, Map allRequestsByClientRequest, List recentSuccesses) 
{
+       protected ClientRequestSchedulerBase(boolean forInserts, boolean 
forSSKs, Map allRequestsByClientRequest, List recentSuccesses) {
                this.isInsertScheduler = forInserts;
                this.isSSKScheduler = forSSKs;
-               this.pendingKeys = pendingKeys;
                this.allRequestsByClientRequest = allRequestsByClientRequest;
                this.recentSuccesses = recentSuccesses;
                priorities = new 
SortedVectorByNumber[RequestStarter.NUMBER_OF_PRIORITY_CLASSES];
                logMINOR = Logger.shouldLog(Logger.MINOR, 
ClientRequestSchedulerBase.class);
        }

-       /**
-        * Register a pending key to an already-registered request. This is 
necessary if we've
-        * already registered a SendableGet, but we later add some more keys to 
it.
-        */
-       void addPendingKey(Key nodeKey, SendableGet getter, ObjectContainer 
container) {
-               logMINOR = Logger.shouldLog(Logger.MINOR, 
ClientRequestSchedulerBase.class);
-               if(logMINOR)
-                       Logger.minor(this, "Adding pending key "+nodeKey+" for 
"+getter);
-               synchronized(pendingKeys) {
-                       Object o = pendingKeys.get(nodeKey);
-                       if(o == null) {
-                               pendingKeys.put(nodeKey, getter);
-                       } else if(o instanceof SendableGet) {
-                               SendableGet oldGet = (SendableGet) o;
-                               if(oldGet != getter) {
-                                       pendingKeys.put(nodeKey, new 
SendableGet[] { oldGet, getter });
-                               }
-                       } else {
-                               SendableGet[] gets = (SendableGet[]) o;
-                               boolean found = false;
-                               for(int j=0;j<gets.length;j++) {
-                                       if(gets[j] == getter) {
-                                               found = true;
-                                               break;
-                                       }
-                               }
-                               if(!found) {
-                                       SendableGet[] newGets = new 
SendableGet[gets.length+1];
-                                       System.arraycopy(gets, 0, newGets, 0, 
gets.length);
-                                       newGets[gets.length] = getter;
-                                       pendingKeys.put(nodeKey, newGets);
-                               }
-                       }
-               }
-       }
-
-       public boolean removePendingKey(SendableGet getter, boolean complain, 
Key key, ObjectContainer container) {
-               if(logMINOR)
-                       Logger.minor(this, "Removing pending key: "+getter+" 
for "+key);
-               boolean dropped = false;
-               Object o;
-               /*
-                * Because arrays are not basic types, 
pendingKeys.activationDepth(1) means that
-                * the SendableGet's returned here will be activated to depth 
1, even if they were
-                * within a SendableGet[]. Tested as of 21/05/08.
-                */
-               synchronized(pendingKeys) {
-                       o = pendingKeys.get(key);
-                       if(o == null) {
-                               if(complain)
-                                       Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (no such key)");
-                       } else if(o instanceof SendableGet) {
-                               SendableGet oldGet = (SendableGet) o;
-                               if(oldGet != getter) {
-                                       if(complain)
-                                               Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (1 getter)");
-                               } else {
-                                       dropped = true;
-                                       pendingKeys.remove(key);
-                                       if(logMINOR)
-                                               Logger.minor(this, "Removed 
only getter (1) for "+key, new Exception("debug"));
-                               }
-                       } else {
-                               SendableGet[] gets = (SendableGet[]) o;
-                               final int getsLength = gets.length;
-                               SendableGet[] newGets = new 
SendableGet[getsLength > 1 ? getsLength-1 : 0];
-                               boolean found = false;
-                               int x = 0;
-                               for(int j=0;j<getsLength;j++) {
-                                       if(gets[j] == getter) {
-                                               found = true;
-                                               dropped = true;
-                                               continue;
-                                       }
-                                       if(x == newGets.length) {
-                                               if(!found) {
-                                                       if(complain)
-                                                               
Logger.normal(this, "Not found: "+getter+" for "+key+" removing ("+getsLength+" 
getters)");
-                                                       return false; // not 
here
-                                               }
-                                       }
-                                       if(gets[j] == null) continue;
-                                       if(gets[j].isCancelled(container)) 
continue;
-                                       newGets[x++] = gets[j];
-                               }
-                               if(x == 0) {
-                                       pendingKeys.remove(key);
-                                       if(logMINOR)
-                                               Logger.minor(this, "Removed 
only getter (2) for "+key, new Exception("debug"));
-                               } else if(x == 1) {
-                                       pendingKeys.put(key, newGets[0]);
-                               } else {
-                                       if(x != getsLength-1) {
-                                               SendableGet[] newNewGets = new 
SendableGet[x];
-                                               System.arraycopy(newGets, 0, 
newNewGets, 0, x);
-                                               newGets = newNewGets;
-                                       }
-                                       pendingKeys.put(key, newGets);
-                               }
-                       }
-               }
-               return dropped;
-       }
-
-       public SendableGet[] removePendingKey(Key key, ObjectContainer 
container) {
-               Object o;
-               final SendableGet[] gets;
-               synchronized(pendingKeys) {
-                       o = pendingKeys.remove(key);
-               }
-               if(o == null) return null;
-               if(o instanceof SendableGet) {
-                       gets = new SendableGet[] { (SendableGet) o };
-                       if(logMINOR)
-                               Logger.minor(this, "Removing all pending keys 
for "+key+" (1)", new Exception("debug"));
-               } else {
-                       gets = (SendableGet[]) o;
-                       if(logMINOR)
-                               Logger.minor(this, "Removing all pending keys 
for "+key+" ("+gets.length+")", new Exception("debug"));
-               }
-               return gets;
-       }
-
-       public boolean anyWantKey(Key key, ObjectContainer container) {
-               synchronized(pendingKeys) {
-                       return pendingKeys.get(key) != null;
-               }
-       }
-
-       public short getKeyPrio(Key key, short priority, ObjectContainer 
container) {
-               synchronized(pendingKeys) {
-                       Object o = pendingKeys.get(key);
-                       if(o == null) {
-                               // Blah
-                       } else if(o instanceof SendableGet) {
-                               short p = 
((SendableGet)o).getPriorityClass(container);
-                               if(p < priority) priority = p;
-                       } else { // if(o instanceof SendableGet[]) {
-                               SendableGet[] gets = (SendableGet[]) o;
-                               for(int i=0;i<gets.length;i++) {
-                                       short p = 
gets[i].getPriorityClass(container);
-                                       if(p < priority) priority = p;
-                               }
-                       }
-               }
-               return priority;
-       }
-
-       public SendableGet[] getClientsForPendingKey(Key key, ObjectContainer 
container) {
-               Object o;
-               synchronized(pendingKeys) {
-                       o = pendingKeys.get(key);
-               }
-               if(o == null) {
-                       return null;
-               } else if(o instanceof SendableGet) {
-                       SendableGet get = (SendableGet) o;
-                       return new SendableGet[] { get };
-               } else {
-                       return (SendableGet[]) o;
-               }
-       }
-
-       protected boolean inPendingKeys(SendableRequest req, Key key, 
ObjectContainer container) {
-               Object o;
-               synchronized(pendingKeys) {
-                       o = pendingKeys.get(key);
-               }
-               if(o == null) {
-                       return false;
-               } else if(o instanceof SendableGet) {
-                       return o == req;
-               } else {
-                       SendableGet[] gets = (SendableGet[]) o;
-                       for(int i=0;i<gets.length;i++)
-                               if(gets[i] == req) return true;
-               }
-               return false;
-       }
-
-       public long countQueuedRequests(ObjectContainer container) {
-               if(pendingKeys != null)
-                       return pendingKeys.size();
-               else return 0;
-       }
-       
        void innerRegister(SendableRequest req, RandomSource random, 
ObjectContainer container) {
                if(req.persistent() != persistent())
                        throw new IllegalArgumentException("innerRegister for 
persistence="+req.persistent()+" but our persistence is "+persistent());
@@ -280,8 +88,6 @@
                if(logMINOR) Logger.minor(this, "Registered "+req+" on 
prioclass="+prio+", retrycount="+retryCount+" v.size()="+v.size());
        }

-       protected abstract Set 
makeSetForAllRequestsByClientRequest(ObjectContainer container);
-
        void addToGrabArray(short priorityClass, int retryCount, int rc, Object 
client, ClientRequester cr, SendableRequest req, RandomSource random, 
ObjectContainer container) {
                if((priorityClass > RequestStarter.MINIMUM_PRIORITY_CLASS) || 
(priorityClass < RequestStarter.MAXIMUM_PRIORITY_CLASS))
                        throw new IllegalStateException("Invalid priority: 
"+priorityClass+" - range is "+RequestStarter.MAXIMUM_PRIORITY_CLASS+" (most 
important) to "+RequestStarter.MINIMUM_PRIORITY_CLASS+" (least important)");
@@ -395,5 +201,35 @@
                        }
                }
        }
+       
+       public short getKeyPrio(Key key, short priority, ObjectContainer 
container) {
+               SendableGet[] getters = getClientsForPendingKey(key, container);
+               if(getters == null) return priority;
+               for(int i=0;i<getters.length;i++) {
+                       if(persistent())
+                               container.activate(getters[i], 1);
+                       short prio = getters[i].getPriorityClass(container);
+                       if(prio < priority) priority = prio;
+                       if(persistent())
+                               container.deactivate(getters[i], 1);
+               }
+               return priority;
+       }
+       
+       public abstract long countQueuedRequests(ObjectContainer container);
+       
+       protected abstract boolean inPendingKeys(SendableGet req, Key key, 
ObjectContainer container);
+       
+       public abstract SendableGet[] getClientsForPendingKey(Key key, 
ObjectContainer container);
+       
+       public abstract boolean anyWantKey(Key key, ObjectContainer container);
+       
+       public abstract SendableGet[] removePendingKey(Key key, ObjectContainer 
container);
+       
+       public abstract boolean removePendingKey(SendableGet getter, boolean 
complain, Key key, ObjectContainer container);
+       
+       abstract void addPendingKey(Key key, SendableGet getter, 
ObjectContainer container);
+       
+       protected abstract Set 
makeSetForAllRequestsByClientRequest(ObjectContainer container);

 }

Modified: 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java  
    2008-07-04 13:58:46 UTC (rev 20992)
+++ 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java  
    2008-07-04 14:28:59 UTC (rev 20993)
@@ -11,6 +11,7 @@
 import com.db4o.ObjectContainer;
 import com.db4o.ObjectSet;
 import com.db4o.query.Predicate;
+import com.db4o.query.Query;
 import com.db4o.types.Db4oList;
 import com.db4o.types.Db4oMap;

@@ -92,7 +93,7 @@
        }

        ClientRequestSchedulerCore(Node node, boolean forInserts, boolean 
forSSKs, ObjectContainer selectorContainer, long cooldownTime) {
-               super(forInserts, forSSKs, forInserts ? null : 
selectorContainer.ext().collections().newHashMap(1024), 
selectorContainer.ext().collections().newHashMap(32), 
selectorContainer.ext().collections().newLinkedList());
+               super(forInserts, forSSKs, 
selectorContainer.ext().collections().newHashMap(32), 
selectorContainer.ext().collections().newLinkedList());
                this.nodeDBHandle = node.nodeDBHandle;
                if(!forInserts) {
                        this.persistentCooldownQueue = new 
PersistentCooldownQueue();
@@ -103,14 +104,10 @@

        private void onStarted(ObjectContainer container, long cooldownTime, 
ClientRequestScheduler sched, ClientContext context) {
                System.err.println("insert scheduler: "+isInsertScheduler);
-               if(pendingKeys == null)
-                       System.err.println("pendingKeys is null");
                if(allRequestsByClientRequest == null)
                        System.err.println("allRequestsByClientRequest is 
null");
                if(recentSuccesses == null)
                        System.err.println("recentSuccesses is null");
-               if(!isInsertScheduler)
-                       ((Db4oMap)pendingKeys).activationDepth(1);
                ((Db4oMap)allRequestsByClientRequest).activationDepth(1);
                ((Db4oList)recentSuccesses).activationDepth(1);
                if(!isInsertScheduler) {
@@ -191,6 +188,8 @@
                                container.delete(req);
                                continue;
                        }
+                       if(logMINOR)
+                               Logger.minor(this, "Adding old request: "+req);
                        sched.addToStarterQueue(req);
                }
 //             if(count > ClientRequestScheduler.MAX_STARTER_QUEUE_SIZE)
@@ -276,7 +275,7 @@
                                        Logger.minor(this, "Storing "+ret+" for 
"+req);
                                if((ctr++ & 15) == 0) {
                                        // This check is quite expensive, don't 
do it all the time.
-                                       if((req instanceof SendableGet) && 
!inPendingKeys(req, key, container)) {
+                                       if((req instanceof SendableGet) && 
!inPendingKeys((SendableGet)req, key, container)) {
                                                Logger.error(this, "Selected 
key not in pendingKeys: key "+key+" for "+req);
                                        }
                                }
@@ -286,7 +285,7 @@
                        return ret;
                }
        }
-       
+
        SendableRequest removeFirstInner(int fuzz, RandomSource random, 
OfferedKeysList[] offeredKeys, RequestStarter starter, 
ClientRequestSchedulerNonPersistent schedTransient, boolean transientOnly, 
boolean notTransient, short maxPrio, int retryCount, ClientContext context, 
ObjectContainer container) {
                // Priorities start at 0
                if(logMINOR) Logger.minor(this, "removeFirst()");
@@ -609,5 +608,148 @@
                return new Db4oSet(container, 1);
        }

+       public long countQueuedRequests(ObjectContainer container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(item.nodeDBHandle == nodeDBHandle) return 
true;
+                               return false;
+                       }
+               });
+               return pending.size();
+       }
+
+       protected boolean inPendingKeys(SendableGet req, final Key key, 
ObjectContainer container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               if(pending.hasNext()) {
+                       PendingKeyItem item = (PendingKeyItem) pending.next();
+                       return item.hasGetter(req);
+               }
+               Logger.error(this, "Key not in pendingKeys at all");
+//             Key copy = key.cloneKey();
+//             addPendingKey(copy, req, container);
+//             container.commit();
+//             pending = container.query(new Predicate() {
+//                     public boolean match(PendingKeyItem item) {
+//                             if(!key.equals(item.key)) return false;
+//                             if(item.nodeDBHandle != nodeDBHandle) return 
false;
+//                             return true;
+//                     }
+//             });
+//             if(!pending.hasNext()) {
+//                     Logger.error(this, "INDEXES BROKEN!!!");
+//             } else {
+//                     PendingKeyItem item = (PendingKeyItem) (pending.next());
+//                     Key k = item.key;
+//                     container.delete(item);
+//                     Logger.error(this, "Indexes work");
+//             }
+               return false;
+       }
+
+       public SendableGet[] getClientsForPendingKey(final Key key, 
ObjectContainer container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               if(pending.hasNext()) {
+                       PendingKeyItem item = (PendingKeyItem) pending.next();
+                       return item.getters();
+               }
+               return null;
+       }
+
+       public boolean anyWantKey(final Key key, ObjectContainer container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               return pending.hasNext();
+       }
+
+       public SendableGet[] removePendingKey(final Key key, ObjectContainer 
container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               if(pending.hasNext()) {
+                       PendingKeyItem item = (PendingKeyItem) pending.next();
+                       SendableGet[] getters = item.getters();
+                       container.delete(item);
+                       return getters;
+               }
+               return null;
+       }
+
+       public boolean removePendingKey(SendableGet getter, boolean complain, 
final Key key, ObjectContainer container) {
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               if(pending.hasNext()) {
+                       PendingKeyItem item = (PendingKeyItem) pending.next();
+                       boolean ret = item.removeGetter(getter);
+                       if(item.isEmpty()) {
+                               container.delete(item);
+                       } else {
+                               container.set(item);
+                       }
+                       return ret;
+               }
+               return false;
+       }
+
+       protected void addPendingKey(final Key key, SendableGet getter, 
ObjectContainer container) {
+               if(logMINOR)
+                       Logger.minor(this, "Adding pending key for "+key+" for 
"+getter);
+               long startTime = System.currentTimeMillis();
+//             Query query = container.query();
+//             query.constrain(PendingKeyItem.class);
+//             query.descend("key").constrain(key);
+//             query.descend("nodeDBHandle").constrain(new Long(nodeDBHandle));
+//             ObjectSet pending = query.execute();
+               
+               // Native version seems to be faster, at least for a few 
thousand items...
+               // I'm not sure whether it's using the index though, we may 
need to reconsider for larger queues... FIXME
+               
+               ObjectSet pending = container.query(new Predicate() {
+                       public boolean match(PendingKeyItem item) {
+                               if(!key.equals(item.key)) return false;
+                               if(item.nodeDBHandle != nodeDBHandle) return 
false;
+                               return true;
+                       }
+               });
+               long endTime = System.currentTimeMillis();
+               if(endTime - startTime > 1000)
+                       Logger.error(this, "Query took "+(endTime - 
startTime)+"ms");
+               else if(logMINOR)
+                       Logger.minor(this, "Query took "+(endTime - 
startTime)+"ms");
+               if(pending.hasNext()) {
+                       PendingKeyItem item = (PendingKeyItem) pending.next();
+                       item.addGetter(getter);
+                       container.set(item);
+               } else {
+                       PendingKeyItem item = new PendingKeyItem(key, getter, 
nodeDBHandle);
+                       container.set(item);
+               }
+       }
+
 }


Modified: 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerNonPersistent.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerNonPersistent.java
     2008-07-04 13:58:46 UTC (rev 20992)
+++ 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerNonPersistent.java
     2008-07-04 14:28:59 UTC (rev 20993)
@@ -6,16 +6,23 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Map;
 import java.util.Set;

 import com.db4o.ObjectContainer;

+import freenet.keys.Key;
+import freenet.node.SendableGet;
+import freenet.node.SendableRequest;
+import freenet.support.Logger;
+
 /**
  * Parallel scheduler structures for non-persistent requests.
  * @author toad
  */
 class ClientRequestSchedulerNonPersistent extends ClientRequestSchedulerBase {

+       private boolean logMINOR;
        /**
         * Structure:
         * array (by priority) -> // one element per possible priority
@@ -26,9 +33,19 @@
         */
        final LinkedList /* <BaseSendableGet> */ recentSuccesses;

+       /** All pending gets by key. Used to automatically satisfy pending 
requests when either the key is fetched by
+        * an overlapping request, or it is fetched by a request from another 
node. Operations on this are synchronized on
+        * itself. */
+       protected final Map /* <Key, SendableGet[]> */ pendingKeys;
+       
        ClientRequestSchedulerNonPersistent(ClientRequestScheduler sched) {
-               super(sched.isInsertScheduler, sched.isSSKScheduler, 
sched.isInsertScheduler ? null : new HashMap(), new HashMap(), new 
LinkedList());
+               super(sched.isInsertScheduler, sched.isSSKScheduler, new 
HashMap(), new LinkedList());
                recentSuccesses = new LinkedList();
+               if(sched.isInsertScheduler)
+                       pendingKeys = null;
+               else
+                       pendingKeys = new HashMap();
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
        }

        boolean persistent() {
@@ -42,5 +59,192 @@
        protected Set makeSetForAllRequestsByClientRequest(ObjectContainer 
ignored) {
                return new HashSet();
        }
+       
+       /**
+        * Register a pending key to an already-registered request. This is 
necessary if we've
+        * already registered a SendableGet, but we later add some more keys to 
it.
+        */
+       void addPendingKey(Key nodeKey, SendableGet getter, ObjectContainer 
container) {
+               logMINOR = Logger.shouldLog(Logger.MINOR, 
ClientRequestSchedulerBase.class);
+               if(logMINOR)
+                       Logger.minor(this, "Adding pending key "+nodeKey+" for 
"+getter);
+               synchronized(pendingKeys) {
+                       Object o = pendingKeys.get(nodeKey);
+                       if(o == null) {
+                               pendingKeys.put(nodeKey, getter);
+                       } else if(o instanceof SendableGet) {
+                               SendableGet oldGet = (SendableGet) o;
+                               if(oldGet != getter) {
+                                       pendingKeys.put(nodeKey, new 
SendableGet[] { oldGet, getter });
+                               }
+                       } else {
+                               SendableGet[] gets = (SendableGet[]) o;
+                               boolean found = false;
+                               for(int j=0;j<gets.length;j++) {
+                                       if(gets[j] == getter) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if(!found) {
+                                       SendableGet[] newGets = new 
SendableGet[gets.length+1];
+                                       System.arraycopy(gets, 0, newGets, 0, 
gets.length);
+                                       newGets[gets.length] = getter;
+                                       pendingKeys.put(nodeKey, newGets);
+                               }
+                       }
+               }
+       }

+       public boolean removePendingKey(SendableGet getter, boolean complain, 
Key key, ObjectContainer container) {
+               if(logMINOR)
+                       Logger.minor(this, "Removing pending key: "+getter+" 
for "+key);
+               boolean dropped = false;
+               Object o;
+               /*
+                * Because arrays are not basic types, 
pendingKeys.activationDepth(1) means that
+                * the SendableGet's returned here will be activated to depth 
1, even if they were
+                * within a SendableGet[]. Tested as of 21/05/08.
+                */
+               synchronized(pendingKeys) {
+                       o = pendingKeys.get(key);
+                       if(o == null) {
+                               if(complain)
+                                       Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (no such key)");
+                       } else if(o instanceof SendableGet) {
+                               SendableGet oldGet = (SendableGet) o;
+                               if(oldGet != getter) {
+                                       if(complain)
+                                               Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (1 getter)");
+                               } else {
+                                       dropped = true;
+                                       pendingKeys.remove(key);
+                                       if(logMINOR)
+                                               Logger.minor(this, "Removed 
only getter (1) for "+key, new Exception("debug"));
+                               }
+                       } else {
+                               SendableGet[] gets = (SendableGet[]) o;
+                               final int getsLength = gets.length;
+                               SendableGet[] newGets = new 
SendableGet[getsLength > 1 ? getsLength-1 : 0];
+                               boolean found = false;
+                               int x = 0;
+                               for(int j=0;j<getsLength;j++) {
+                                       if(gets[j] == getter) {
+                                               found = true;
+                                               dropped = true;
+                                               continue;
+                                       }
+                                       if(x == newGets.length) {
+                                               if(!found) {
+                                                       if(complain)
+                                                               
Logger.normal(this, "Not found: "+getter+" for "+key+" removing ("+getsLength+" 
getters)");
+                                                       return false; // not 
here
+                                               }
+                                       }
+                                       if(gets[j] == null) continue;
+                                       if(gets[j].isCancelled(container)) 
continue;
+                                       newGets[x++] = gets[j];
+                               }
+                               if(x == 0) {
+                                       pendingKeys.remove(key);
+                                       if(logMINOR)
+                                               Logger.minor(this, "Removed 
only getter (2) for "+key, new Exception("debug"));
+                               } else if(x == 1) {
+                                       pendingKeys.put(key, newGets[0]);
+                               } else {
+                                       if(x != getsLength-1) {
+                                               SendableGet[] newNewGets = new 
SendableGet[x];
+                                               System.arraycopy(newGets, 0, 
newNewGets, 0, x);
+                                               newGets = newNewGets;
+                                       }
+                                       pendingKeys.put(key, newGets);
+                               }
+                       }
+               }
+               return dropped;
+       }
+
+       public SendableGet[] removePendingKey(Key key, ObjectContainer 
container) {
+               Object o;
+               final SendableGet[] gets;
+               synchronized(pendingKeys) {
+                       o = pendingKeys.remove(key);
+               }
+               if(o == null) return null;
+               if(o instanceof SendableGet) {
+                       gets = new SendableGet[] { (SendableGet) o };
+                       if(logMINOR)
+                               Logger.minor(this, "Removing all pending keys 
for "+key+" (1)", new Exception("debug"));
+               } else {
+                       gets = (SendableGet[]) o;
+                       if(logMINOR)
+                               Logger.minor(this, "Removing all pending keys 
for "+key+" ("+gets.length+")", new Exception("debug"));
+               }
+               return gets;
+       }
+
+       public boolean anyWantKey(Key key, ObjectContainer container) {
+               synchronized(pendingKeys) {
+                       return pendingKeys.get(key) != null;
+               }
+       }
+
+       public short getKeyPrio(Key key, short priority, ObjectContainer 
container) {
+               synchronized(pendingKeys) {
+                       Object o = pendingKeys.get(key);
+                       if(o == null) {
+                               // Blah
+                       } else if(o instanceof SendableGet) {
+                               short p = 
((SendableGet)o).getPriorityClass(container);
+                               if(p < priority) priority = p;
+                       } else { // if(o instanceof SendableGet[]) {
+                               SendableGet[] gets = (SendableGet[]) o;
+                               for(int i=0;i<gets.length;i++) {
+                                       short p = 
gets[i].getPriorityClass(container);
+                                       if(p < priority) priority = p;
+                               }
+                       }
+               }
+               return priority;
+       }
+
+       public SendableGet[] getClientsForPendingKey(Key key, ObjectContainer 
container) {
+               Object o;
+               synchronized(pendingKeys) {
+                       o = pendingKeys.get(key);
+               }
+               if(o == null) {
+                       return null;
+               } else if(o instanceof SendableGet) {
+                       SendableGet get = (SendableGet) o;
+                       return new SendableGet[] { get };
+               } else {
+                       return (SendableGet[]) o;
+               }
+       }
+
+       protected boolean inPendingKeys(SendableGet req, Key key, 
ObjectContainer container) {
+               Object o;
+               synchronized(pendingKeys) {
+                       o = pendingKeys.get(key);
+               }
+               if(o == null) {
+                       return false;
+               } else if(o instanceof SendableGet) {
+                       return o == req;
+               } else {
+                       SendableGet[] gets = (SendableGet[]) o;
+                       for(int i=0;i<gets.length;i++)
+                               if(gets[i] == req) return true;
+               }
+               return false;
+       }
+
+       public long countQueuedRequests(ObjectContainer container) {
+               if(pendingKeys != null)
+                       return pendingKeys.size();
+               else return 0;
+       }
+       
+
 }

Added: branches/db4o/freenet/src/freenet/client/async/PendingKeyItem.java
===================================================================
--- branches/db4o/freenet/src/freenet/client/async/PendingKeyItem.java          
                (rev 0)
+++ branches/db4o/freenet/src/freenet/client/async/PendingKeyItem.java  
2008-07-04 14:28:59 UTC (rev 20993)
@@ -0,0 +1,73 @@
+package freenet.client.async;
+
+import com.db4o.ObjectContainer;
+
+import freenet.keys.Key;
+import freenet.node.SendableGet;
+import freenet.node.SendableRequest;
+
+public class PendingKeyItem {
+       
+       final long nodeDBHandle;
+       final Key key;
+       private SendableGet[] getters;
+       
+       PendingKeyItem(Key key, SendableGet getter, long nodeDBHandle) {
+               this.key = key;
+               this.getters = new SendableGet[] { getter };
+               this.nodeDBHandle = nodeDBHandle;
+       }
+       
+       public void addGetter(SendableGet getter) {
+               for(int i=0;i<getters.length;i++) {
+                       if(getters[i] == getter) return;
+               }
+               SendableGet[] newGetters = new SendableGet[getters.length+1];
+               System.arraycopy(getters, 0, newGetters, 0, getters.length);
+               newGetters[getters.length] = getter;
+               getters = newGetters;
+       }
+       
+       /**
+        * @param getter
+        * @return True if the getter was removed. Caller should check 
isEmpty() afterwards.
+        */
+       public boolean removeGetter(SendableGet getter) {
+               int found = 0;
+               for(int i=0;i<getters.length;i++) {
+                       if(getters[i] == getter) found++;
+               }
+               if(found == 0) return false;
+               if(found == getters.length)
+                       getters = new SendableGet[0];
+               else {
+                       SendableGet[] newGetters = new 
SendableGet[getters.length - found];
+                       int x = 0;
+                       for(int i=0;i<getters.length;i++) {
+                               if(getters[i] == getter) continue;
+                               newGetters[x++] = getters[i];
+                       }
+                       getters = newGetters;
+               }
+               return true;
+       }
+       
+       public boolean isEmpty() {
+               return getters.length == 0;
+       }
+
+       public boolean hasGetter(SendableRequest req) {
+               for(int i=0;i<getters.length;i++)
+                       if(getters[i] == req) return true;
+               return false;
+       }
+
+       public SendableGet[] getters() {
+               return getters;
+       }
+       
+       public void objectOnActivate(ObjectContainer container) {
+               container.activate(key, 5);
+       }
+
+}
\ No newline at end of file

Modified: branches/db4o/freenet/src/freenet/node/Node.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/Node.java    2008-07-04 13:58:46 UTC 
(rev 20992)
+++ branches/db4o/freenet/src/freenet/node/Node.java    2008-07-04 14:28:59 UTC 
(rev 20993)
@@ -723,6 +723,7 @@
                
Db4o.configure().objectClass(freenet.client.async.RegisterMe.class).objectField("core").indexed(true);
                
Db4o.configure().objectClass(freenet.client.async.RegisterMe.class).objectField("key").indexed(true);
                
Db4o.configure().objectClass(freenet.client.async.PersistentCooldownQueueItem.class).objectField("time").indexed(true);
+               
Db4o.configure().objectClass(freenet.client.async.PendingKeyItem.class).objectField("key").indexed(true);
                /** Maybe we want a different query evaluation mode?
                 * At the moment, a big splitfile insert will result in one 
SingleBlockInserter
                 * for every key, which means one RegisterMe for each ... this 
results in a long pause


Reply via email to