package freenet.support; import com.db4o.ObjectContainer; import freenet.client.async.ClientContext; /** * Like RandomGrabArray, but there is an equal chance of any given client's requests being * returned. */ public class SectoredRandomGrabArray implements RemoveRandom, RemoveRandomParent { private static volatile boolean logMINOR; static { Logger.registerLogThresholdCallback(new LogThresholdCallback() { @Override public void shouldUpdate() { logMINOR = Logger.shouldLog(Logger.MINOR, this); } }); } /* * 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; private final RemoveRandomParent parent; public SectoredRandomGrabArray(boolean persistent, ObjectContainer container, RemoveRandomParent parent) { this.persistent = persistent; grabClients = new Object[0]; grabArrays = new RemoveRandomWithObject[0]; this.parent = parent; } /** * Add directly to a RandomGrabArrayWithClient under us. */ public synchronized void add(Object client, RandomGrabArrayItem item, ObjectContainer container) { if(item.persistent() != persistent) throw new IllegalArgumentException("item.persistent()="+item.persistent()+" but array.persistent="+persistent+" item="+item+" array="+this); RandomGrabArrayWithClient rga; 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, this); addElement(client, rga); if(persistent) { container.store(rga); container.store(this); } } else { rga = (RandomGrabArrayWithClient) grabArrays[clientIndex]; if(persistent) container.activate(rga, 1); } if(logMINOR) Logger.minor(this, "Adding "+item+" to RGA "+rga+" for "+client); rga.add(item, container); if(persistent) container.deactivate(rga, 1); if(logMINOR) 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 MAX_EXCLUDED) { 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; } } if(persistent) container.deactivate(rga, 1); continue; } if(persistent) container.deactivate(rga, 1); if(item.isEmpty(container)) continue; return item; } } private synchronized 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; } public boolean persistent() { return persistent; } public int size() { return grabArrays.length; } public void removeFrom(ObjectContainer container) { if(grabArrays != null && grabArrays.length != 0) { for(RemoveRandomWithObject rr : grabArrays) { if(rr != null) { Logger.error(this, "NOT EMPTY REMOVING "+this+" : "+rr); return; } } } container.delete(this); } public void maybeRemove(RemoveRandom r, ObjectContainer container) { int count = 0; synchronized(this) { while(true) { int found = -1; for(int i=0;i 1) Logger.error(this, "Found "+r+" many times in "+this, new Exception("error")); removeElement(found); } else { break; } } } if(count == 0) Logger.error(this, "Not in parent: "+r+" for "+this, new Exception("error")); else if(persistent) { container.store(this); r.removeFrom(container); } } }