Author: toad
Date: 2008-08-28 19:08:32 +0000 (Thu, 28 Aug 2008)
New Revision: 22201

Modified:
   
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java
   branches/db4o/freenet/src/freenet/support/SectoredRandomGrabArray.java
Log:
Use a simple O(n) data structure in SectoredRandomGrabArray.
Justifications included. This is better for persistent requests and only 
becomes a problem with huge numbers.


Modified: 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java  
    2008-08-28 17:38:32 UTC (rev 22200)
+++ 
branches/db4o/freenet/src/freenet/client/async/ClientRequestSchedulerCore.java  
    2008-08-28 19:08:32 UTC (rev 22201)
@@ -437,6 +437,8 @@
                                // Remove it.
                                SectoredRandomGrabArrayWithObject clientGrabber 
= (SectoredRandomGrabArrayWithObject) chosenTracker.getGrabber(req.getClient());
                                if(clientGrabber != null) {
+                                       if(chosenTracker.persistent())
+                                               
container.activate(clientGrabber, 1);
                                        RandomGrabArray baseRGA = 
(RandomGrabArray) clientGrabber.getGrabber(req.getClientRequest());
                                        if(baseRGA != null) {
                                                baseRGA.remove(req, container);

Modified: branches/db4o/freenet/src/freenet/support/SectoredRandomGrabArray.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/SectoredRandomGrabArray.java      
2008-08-28 17:38:32 UTC (rev 22200)
+++ branches/db4o/freenet/src/freenet/support/SectoredRandomGrabArray.java      
2008-08-28 19:08:32 UTC (rev 22201)
@@ -1,10 +1,6 @@
 package freenet.support;

-import java.util.HashMap;
-import java.util.Map;
-
 import com.db4o.ObjectContainer;
-import com.db4o.types.Db4oMap;

 import freenet.client.async.ClientContext;

@@ -14,18 +10,32 @@
  */
 public class SectoredRandomGrabArray implements RemoveRandom {

-       private final Map grabArraysByClient;
+       /*
+        * Yes, this is O(n). No, I don't care.
+        * 
+        * Using a Db4oMap results in stuff getting reactivated during the 
commit
+        * phase, and not deactivated. This makes keeping stuff that shouldn't 
be
+        * activated deactivated impossible, resulting in more memory usage. 
more
+        * Full GC's, more object churn, and hence more CPU usage. Also Db4oMap 
is
+        * deprecated.
+        * 
+        * Using a HashMap populated in objectOnActivate() doesn't work either,
+        * because it ends up comparing deactivated clients with activated ones.
+        * This will result in NPEs, and unnecessary code complexity to fix 
them.
+        * 
+        * IMHO it's not worth bothering with a hashtable if it's less than 1000
+        * or so items anyway. If size does become a problem we will need to 
+        * implement our own activation aware hashtable class, which stores the 
+        * full hashCode, and matches on == object identity, so that we don't 
need
+        * to activate on comparison.
+        */
        private RemoveRandomWithObject[] grabArrays;
+       private Object[] grabClients;
        private final boolean persistent;

        public SectoredRandomGrabArray(boolean persistent, ObjectContainer 
container) {
                this.persistent = persistent;
-               if(persistent) {
-                       // FIXME is this too heavyweight? Maybe we should 
iterate the array or something?
-                       grabArraysByClient = 
container.ext().collections().newHashMap(10);
-                       ((Db4oMap)grabArraysByClient).activationDepth(1); // 
FIXME can we get away with 1??
-               } else
-                       grabArraysByClient = new HashMap();
+               grabClients = new Object[0];
                grabArrays = new RemoveRandomWithObject[0];
        }

@@ -35,22 +45,18 @@
                if(item.persistent() != persistent) throw new 
IllegalArgumentException("item.persistent()="+item.persistent()+" but 
array.persistent="+persistent+" item="+item+" array="+this);
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                RandomGrabArrayWithClient rga;
-               if(!grabArraysByClient.containsKey(client)) {
+               int clientIndex = haveClient(client);
+               if(clientIndex == -1) {
                        if(logMINOR)
                                Logger.minor(this, "Adding new RGAWithClient 
for "+client+" on "+this+" for "+item);
                        rga = new RandomGrabArrayWithClient(client, persistent, 
container);
-                       RemoveRandomWithObject[] newArrays = new 
RemoveRandomWithObject[grabArrays.length+1];
-                       System.arraycopy(grabArrays, 0, newArrays, 0, 
grabArrays.length);
-                       newArrays[grabArrays.length] = rga;
-                       grabArrays = newArrays;
-                       grabArraysByClient.put(client, rga);
+                       addElement(client, rga);
                        if(persistent) {
                                container.set(rga);
-                               container.set(grabArraysByClient);
                                container.set(this);
                        }
                } else {
-                       rga = (RandomGrabArrayWithClient) 
grabArraysByClient.get(client);
+                       rga = (RandomGrabArrayWithClient) 
grabArrays[clientIndex];
                }
                if(logMINOR)
                        Logger.minor(this, "Adding "+item+" to RGA "+rga+" for 
"+client);
@@ -63,12 +69,34 @@
                        Logger.minor(this, "Size now "+grabArrays.length+" on 
"+this);
        }

+       private void addElement(Object client, RemoveRandomWithObject rga) {
+               int len = grabArrays.length;
+               RemoveRandomWithObject[] newArrays = new 
RemoveRandomWithObject[len+1];
+               System.arraycopy(grabArrays, 0, newArrays, 0, len);
+               newArrays[len] = rga;
+               grabArrays = newArrays;
+               
+               Object[] newClients = new Object[len+1];
+               System.arraycopy(grabClients, 0, newClients, 0, len);
+               newClients[len] = client;
+               grabClients = newClients;
+       }
+
+       private synchronized int haveClient(Object client) {
+               for(int i=0;i<grabClients.length;i++) {
+                       if(grabClients[i] == client) return i;
+               }
+               return -1;
+       }
+
        /**
         * Get a grabber. This lets us use things other than 
RandomGrabArrayWithClient's, so don't mix calls
         * to add() with calls to getGrabber/addGrabber!
         */
        public synchronized RemoveRandomWithObject getGrabber(Object client) {
-               return (RemoveRandomWithObject) grabArraysByClient.get(client); 
// auto-activated to depth 1
+               int idx = haveClient(client);
+               if(idx == -1) return null;
+               else return grabArrays[idx];
        }

        /**
@@ -76,13 +104,10 @@
         * to add() with calls to getGrabber/addGrabber!
         */
        public synchronized void addGrabber(Object client, 
RemoveRandomWithObject requestGrabber, ObjectContainer container) {
-               grabArraysByClient.put(client, requestGrabber);
-               RemoveRandomWithObject[] newArrays = new 
RemoveRandomWithObject[grabArrays.length+1];
-               System.arraycopy(grabArrays, 0, newArrays, 0, 
grabArrays.length);
-               newArrays[grabArrays.length] = requestGrabber;
-               grabArrays = newArrays;
+               if(requestGrabber.getObject() != client)
+                       throw new IllegalArgumentException("Client not equal to 
RemoveRandomWithObject's client: client="+client+" rr="+requestGrabber+" his 
object="+requestGrabber.getObject());
+               addElement(client, requestGrabber);
                if(persistent) {
-                       container.set(grabArraysByClient);
                        container.set(this);
                }
        }
@@ -106,8 +131,8 @@
                                                container.activate(client, 1);
                                        if(logMINOR)
                                                Logger.minor(this, "Removing 
only grab array (0) : "+rga+" for "+rga.getObject()+" (is empty)");
-                                       grabArraysByClient.remove(client);
                                        grabArrays = new 
RemoveRandomWithObject[0];
+                                       grabClients = new Object[0];
                                        if(persistent)
                                                container.set(this);
                                }
@@ -128,29 +153,11 @@
                                                Logger.error(this, "NOT 
ACTIVE!!");
                                        if(grabArrays[1-x] == null) {
                                                Logger.error(this, "other rga 
is also null on "+this);
-                                               if(grabArraysByClient == null) {
-                                                       Logger.error(this, "as 
is grabArraysByClient");
-                                                       // Let it NPE
-                                               } else {
-                                                       
if(grabArraysByClient.isEmpty()) {
-                                                               
Logger.error(this, "grabArraysByClient is also empty");
-                                                               return null;
-                                                       }
-                                               }
                                        } else {
                                                RemoveRandomWithObject valid = 
grabArrays[1-x];
-                                               if(grabArraysByClient.size() != 
1) {
-                                                       Logger.error(this, 
"Grab arrays by client size should be 1 (since there is 1 non-null), but is 
"+grabArraysByClient.size());
-                                                       
grabArraysByClient.clear();
-                                                       if(persistent)
-                                                               
container.activate(valid, 1);
-                                                       Object client = 
valid.getObject();
-                                                       if(persistent)
-                                                               
container.activate(client, 1);
-                                                       
grabArraysByClient.put(client, valid);
-                                               }
                                                Logger.error(this, 
"grabArrays["+(1-x)+"] is valid but ["+x+"] is null, correcting...");
                                                grabArrays = new 
RemoveRandomWithObject[] { grabArrays[1-x] };
+                                               grabClients = new Object[] { 
grabClients[1-x] };
                                                continue;
                                        }
                                }
@@ -162,27 +169,18 @@
                                                container.activate(rga, 1);
                                        item = rga.removeRandom(excluding, 
container, context);
                                        if(firstRGA.isEmpty() && rga.isEmpty()) 
{
-                                               Object rgaClient = 
rga.getObject();
-                                               Object firstClient = 
firstRGA.getObject();
-                                               if(persistent) {
-                                                       
container.activate(rgaClient, 1);
-                                                       
container.activate(firstClient, 1);
-                                               }
-                                               
grabArraysByClient.remove(rgaClient);
-                                               
grabArraysByClient.remove(firstClient);
+                                               Object rgaClient = 
grabClients[x];
+                                               Object firstClient = 
grabClients[1-x];
                                                grabArrays = new 
RemoveRandomWithObject[0];
+                                               grabClients = new Object[0];
                                                if(persistent)
                                                        container.set(this);
                                        } else if(firstRGA.isEmpty()) {
                                                if(persistent) {
                                                        
container.activate(firstRGA, 1);
                                                }
-                                               Object firstClient = 
firstRGA.getObject();
-                                               if(persistent) {
-                                                       
container.activate(firstClient, 1);
-                                               }
-                                               
grabArraysByClient.remove(firstClient);
                                                grabArrays = new 
RemoveRandomWithObject[] { rga };
+                                               grabClients = new Object[] { 
grabClients[x] };
                                                if(persistent)
                                                        container.set(this);
                                        }
@@ -214,21 +212,12 @@
                        // Just because the item is cancelled does not 
necessarily mean the whole client is.
                        // E.g. a segment may return cancelled because it is 
decoding, that doesn't mean
                        // other segments are cancelled. So just go around the 
loop in that case.
-                       final int grabArraysLength = grabArrays.length;
                        if(rga.isEmpty()) {
                                if(logMINOR)
                                        Logger.minor(this, "Removing grab array 
"+x+" : "+rga+" (is empty)");
                                Object client = rga.getObject();
+                               removeElement(x);
                                if(persistent)
-                                       container.activate(client, 1);
-                               grabArraysByClient.remove(client);
-                               RemoveRandomWithObject[] newArray = new 
RemoveRandomWithObject[grabArraysLength > 1 ? grabArraysLength-1 : 0];
-                               if(x > 0)
-                                       System.arraycopy(grabArrays, 0, 
newArray, 0, x);
-                               if(x < grabArraysLength-1)
-                                       System.arraycopy(grabArrays, x+1, 
newArray, x, grabArraysLength - (x+1));
-                               grabArrays = newArray;
-                               if(persistent)
                                        container.set(this);
                        }
                        if(item == null) {
@@ -236,7 +225,7 @@
                                        // Hmmm...
                                        excluded++;
                                        if(excluded > MAX_EXCLUDED) {
-                                               Logger.normal(this, "Too many 
sub-arrays are entirely excluded on "+this+" length = "+grabArraysLength, new 
Exception("error"));
+                                               Logger.normal(this, "Too many 
sub-arrays are entirely excluded on "+this+" length = "+grabArrays.length, new 
Exception("error"));
                                                if(persistent)
                                                        
container.deactivate(rga, 1);
                                                return null;
@@ -253,6 +242,24 @@
                }
        }

+       private void removeElement(int x) {
+               final int grabArraysLength = grabArrays.length;
+               int newLen = grabArraysLength > 1 ? grabArraysLength-1 : 0;
+               RemoveRandomWithObject[] newArray = new 
RemoveRandomWithObject[newLen];
+               if(x > 0)
+                       System.arraycopy(grabArrays, 0, newArray, 0, x);
+               if(x < grabArraysLength-1)
+                       System.arraycopy(grabArrays, x+1, newArray, x, 
grabArraysLength - (x+1));
+               grabArrays = newArray;
+               
+               Object[] newClients = new Object[newLen];
+               if(x > 0)
+                       System.arraycopy(grabClients, 0, newClients, 0, x);
+               if(x < grabArraysLength-1)
+                       System.arraycopy(grabClients, x+1, newClients, x, 
grabArraysLength - (x+1));
+               grabClients = newClients;
+       }
+
        public synchronized boolean isEmpty() {
                return grabArrays.length == 0;
        }


Reply via email to