Author: toad
Date: 2008-02-15 22:10:27 +0000 (Fri, 15 Feb 2008)
New Revision: 17963
Added:
trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
Modified:
trunk/freenet/src/freenet/support/Fields.java
Log:
RequestCooldownQueue: Keys will be queued here when they have been recently
requested enough times, so they are not re-requested for a fixed period.
It would be fairly pointless to rerequest them with ULPRs.
Added: trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
===================================================================
--- trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
(rev 0)
+++ trunk/freenet/src/freenet/client/async/RequestCooldownQueue.java
2008-02-15 22:10:27 UTC (rev 17963)
@@ -0,0 +1,214 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.client.async;
+
+import freenet.keys.Key;
+import freenet.support.Fields;
+import freenet.support.Logger;
+
+/**
+ * Queue of keys which have been recently requested, which we have
unregistered for a fixed period.
+ * They won't be requested for a while, although we still have ULPR
subscriptions set up for them.
+ *
+ * We add to the end, remove from the beginning, and occasionally remove from
the middle. It's a
+ * circular buffer, we expand it if necessary.
+ * @author toad
+ */
+public class RequestCooldownQueue {
+
+ /** keys which have been put onto the cooldown queue */
+ private Key[] keys;
+ /** times at which keys will be valid again */
+ private long[] times;
+ /** count of keys removed from middle i.e. holes */
+ int holes;
+ /** location of first (chronologically) key */
+ int ptr;
+ /** location of last key (may be < ptr if wrapped around) */
+ int endPtr;
+
+ static final int MIN_SIZE = 1024;
+
+ final long cooldownTime;
+
+ RequestCooldownQueue(long cooldownTime) {
+ keys = new Key[MIN_SIZE];
+ times = new long[MIN_SIZE];
+ holes = 0;
+ ptr = 0;
+ endPtr = 0;
+ this.cooldownTime = cooldownTime;
+ }
+
+ /**
+ * Add a key to the end of the queue. Returns the time at which it will
be valid again.
+ */
+ synchronized long add(Key key) {
+ 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);
+ return removeTime;
+ }
+
+ private synchronized long getLastTime() {
+ if(ptr == endPtr) return -1;
+ if(endPtr > 0) return times[endPtr-1];
+ return times[times.length-1];
+ }
+
+ private synchronized void add(Key key, long removeTime) {
+ int ptr = endPtr;
+ if(endPtr > ptr) {
+ if(endPtr == keys.length-1) {
+ // Last key
+ if(ptr == 0) {
+ // No room
+ expandQueue();
+ add(key);
+ return;
+ } else {
+ // Wrap around
+ endPtr = 0;
+ }
+ } else {
+ endPtr++;
+ }
+ } else if(endPtr < ptr){
+ if(endPtr == ptr - 1) {
+ expandQueue();
+ add(key);
+ return;
+ } else {
+ endPtr++;
+ }
+ }
+ keys[ptr] = key;
+ times[ptr] = removeTime;
+ return;
+ }
+
+ /**
+ * Remove a key whose cooldown time has passed.
+ * @return Either a Key or null if no keys have passed their cooldown
time.
+ */
+ synchronized Key removeKeyBefore(long now) {
+ while(true) {
+ if(ptr == endPtr) return null;
+ long time = times[ptr];
+ if(time > now) return null;
+ Key key = keys[ptr];
+ if(key == null) {
+ times[ptr] = 0;
+ ptr++;
+ holes--;
+ if(ptr == times.length) ptr = 0;
+ continue;
+ }
+ return key;
+ }
+ }
+
+ /**
+ * @return True if the key was found.
+ */
+ synchronized boolean removeKey(Key key, long time) {
+ int idx = -1;
+ if(endPtr > ptr) {
+ idx = Fields.binarySearch(times, time, ptr, endPtr);
+ } else if(endPtr == ptr) {
+ return false;
+ } else { // endPtr < ptr
+ // FIXME: ARGH! Java doesn't provide binarySearch with
from and to!
+ if(ptr != times.length - 1)
+ idx = Fields.binarySearch(times, time, ptr,
times.length - 1);
+ if(idx < 0 && ptr != 0)
+ idx = Fields.binarySearch(times, time, 0,
endPtr);
+ }
+ if(idx < 0) return false;
+ if(keys[idx] == key) {
+ keys[idx] = null;
+ return true;
+ }
+ // Try backwards first
+ int nidx = idx;
+ while(true) {
+ if(times[nidx] != time) break;
+ if(keys[nidx] == key) {
+ keys[nidx] = null;
+ return true;
+ }
+ if(nidx == ptr) break;
+ nidx--;
+ if(nidx == -1) nidx = times.length;
+ }
+ nidx = idx;
+ // Now try forwards
+ while(true) {
+ if(times[nidx] != time) break;
+ if(keys[nidx] == key) {
+ keys[nidx] = null;
+ return true;
+ }
+ if(nidx == ptr) break;
+ nidx++;
+ if(nidx == times.length) nidx = 0;
+ }
+ return false;
+ }
+
+ /**
+ * Allocate a new queue, and compact while copying.
+ */
+ private synchronized void expandQueue() {
+ int newSize = (keys.length - holes) * 2;
+ // FIXME reuse the old buffers if it fits
+ Key[] newKeys = new Key[newSize];
+ long[] newTimes = new long[newSize];
+ // Reset ptr to 0, and remove holes.
+ int x = 0;
+ long lastTime = -1;
+ if(endPtr > ptr) {
+ for(int i=ptr;i<endPtr;i++) {
+ if(keys[i] == null) continue;
+ newKeys[x] = keys[i];
+ newTimes[x] = times[i];
+ if(lastTime > times[i])
+ Logger.error(this,
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but
lastTime="+lastTime);
+ lastTime = times[i];
+ x++;
+ }
+ } else if(endPtr < ptr) {
+ for(int i=ptr;i<keys.length;i++) {
+ if(keys[i] == null) continue;
+ newKeys[x] = keys[i];
+ newTimes[x] = times[i];
+ if(lastTime > times[i])
+ Logger.error(this,
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but
lastTime="+lastTime);
+ lastTime = times[i];
+ x++;
+ }
+ for(int i=0;i<endPtr;i++) {
+ if(keys[i] == null) continue;
+ newKeys[x] = keys[i];
+ newTimes[x] = times[i];
+ if(lastTime > times[i])
+ Logger.error(this,
"RequestCooldownQueue INCONSISTENCY: times["+i+"] = times[i] but
lastTime="+lastTime);
+ lastTime = times[i];
+ x++;
+ }
+ } else /* endPtr == ptr */ {
+ Logger.error(this, "RequestCooldownQueue: expandQueue()
called with endPtr == ptr == "+ptr+" !!");
+ return;
+ }
+ holes = 0;
+ ptr = 0;
+ keys = newKeys;
+ times = newTimes;
+ endPtr = x;
+ }
+
+}
Modified: trunk/freenet/src/freenet/support/Fields.java
===================================================================
--- trunk/freenet/src/freenet/support/Fields.java 2008-02-15 20:30:00 UTC
(rev 17962)
+++ trunk/freenet/src/freenet/support/Fields.java 2008-02-15 22:10:27 UTC
(rev 17963)
@@ -695,4 +695,33 @@
return bytesToDoubles(data, 0, data.length);
}
+ /**
+ * Assumes the array is sorted in ascending order, [begin] is lowest
and [end] is highest.
+ */
+ public static int binarySearch(long[] values, long key, int origBegin,
int origEnd) {
+ int begin = origBegin;
+ int end = origEnd;
+ while(true) {
+ int middle = (begin + end) / 2;
+ System.out.println("begin="+begin+" end="+end+"
middle="+middle);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ if(values[middle] == key)
+ return middle;
+
+ if(values[middle] > key) {
+ end = middle;
+ if(end - begin <= 1) {
+ return -middle-1;
+ }
+ } else if(values[middle] < key) {
+ begin = middle;
+ if(end - begin <= 1)
+ return -end-1;
+ }
+ }
+ }
+
}