2006-02-20 jrandom
* Major SSU and router tuning to reduce contention, memory usage, and GC churn. There are still issues to be worked out, but this should be a substantial improvement. * Modified the optional netDb harvester task to support choosing whether to use (non-anonymous) direct connections or (anonymous) exploratory tunnels to do the harvesting. Harvesting itself is enabled via the advanced config "netDb.shouldHarvest=true" (default is false) and the connection type can be chosen via "netDb.harvestDirectly=false" (default is false).
This commit is contained in:
@ -250,6 +250,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
+ from + " and " + to);
|
||||
}
|
||||
|
||||
// boo, hiss! shouldn't need this - the streaming lib should be configurable, but
|
||||
// somehow the inactivity timer is sometimes failing to get triggered properly
|
||||
//i2ps.setReadTimeout(2*60*1000);
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
byte[] buffer = ba.getData(); // new byte[NETWORK_BUFFER_SIZE];
|
||||
try {
|
||||
|
@ -71,7 +71,9 @@ public class ReseedHandler {
|
||||
seedURL = DEFAULT_SEED_URL;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
String content = new String(readURL(dir));
|
||||
byte contentRaw[] = readURL(dir);
|
||||
if (contentRaw == null) return;
|
||||
String content = new String(contentRaw);
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
|
@ -140,6 +140,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
loadConfig(options);
|
||||
_sessionId = null;
|
||||
_leaseSet = null;
|
||||
_context.statManager().createRateStat("client.availableMessages", "How many messages are available for the current client", "ClientMessages", new long[] { 60*1000, 10*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,11 +300,17 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*
|
||||
*/
|
||||
public byte[] receiveMessage(int msgId) throws I2PSessionException {
|
||||
int remaining = 0;
|
||||
MessagePayloadMessage msg = null;
|
||||
synchronized (_availableMessages) {
|
||||
msg = (MessagePayloadMessage) _availableMessages.remove(new Long(msgId));
|
||||
remaining = _availableMessages.size();
|
||||
}
|
||||
_context.statManager().addRateData("client.availableMessages", remaining, 0);
|
||||
if (msg == null) {
|
||||
_log.error("Receive message " + msgId + " had no matches, remaining=" + remaining);
|
||||
return null;
|
||||
}
|
||||
if (msg == null) return null;
|
||||
return msg.getPayload().getUnencryptedData();
|
||||
}
|
||||
|
||||
@ -339,9 +346,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
* Recieve a payload message and let the app know its available
|
||||
*/
|
||||
public void addNewMessage(MessagePayloadMessage msg) {
|
||||
Long mid = new Long(msg.getMessageId());
|
||||
int avail = 0;
|
||||
synchronized (_availableMessages) {
|
||||
_availableMessages.put(new Long(msg.getMessageId()), msg);
|
||||
_availableMessages.put(mid, msg);
|
||||
avail = _availableMessages.size();
|
||||
}
|
||||
_context.statManager().addRateData("client.availableMessages", avail, 0);
|
||||
long id = msg.getMessageId();
|
||||
byte data[] = msg.getPayload().getUnencryptedData();
|
||||
if ((data == null) || (data.length <= 0)) {
|
||||
@ -354,20 +365,23 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Notified availability for session " + _sessionId + ", message " + id);
|
||||
}
|
||||
SimpleTimer.getInstance().addEvent(new VerifyUsage(id), 30*1000);
|
||||
SimpleTimer.getInstance().addEvent(new VerifyUsage(mid), 30*1000);
|
||||
}
|
||||
private class VerifyUsage implements SimpleTimer.TimedEvent {
|
||||
private long _msgId;
|
||||
public VerifyUsage(long id) { _msgId = id; }
|
||||
private Long _msgId;
|
||||
public VerifyUsage(Long id) { _msgId = id; }
|
||||
public void timeReached() {
|
||||
MessagePayloadMessage removed = null;
|
||||
int remaining = 0;
|
||||
synchronized (_availableMessages) {
|
||||
removed = (MessagePayloadMessage)_availableMessages.remove(new Long(_msgId));
|
||||
removed = (MessagePayloadMessage)_availableMessages.remove(_msgId);
|
||||
remaining = _availableMessages.size();
|
||||
}
|
||||
if (removed != null) {
|
||||
_log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed + ": remaining: " + remaining);
|
||||
_context.statManager().addRateData("client.availableMessages", remaining, 0);
|
||||
}
|
||||
if (removed != null)
|
||||
_log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class AvailabilityNotifier implements Runnable {
|
||||
|
@ -283,11 +283,12 @@ public class ElGamalAESEngine {
|
||||
try {
|
||||
SessionKey newKey = null;
|
||||
Hash readHash = null;
|
||||
List tags = new ArrayList();
|
||||
List tags = null;
|
||||
|
||||
//ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
|
||||
int cur = 0;
|
||||
long numTags = DataHelper.fromLong(decrypted, cur, 2);
|
||||
if (numTags > 0) tags = new ArrayList((int)numTags);
|
||||
cur += 2;
|
||||
//_log.debug("# tags: " + numTags);
|
||||
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
|
||||
@ -326,7 +327,8 @@ public class ElGamalAESEngine {
|
||||
|
||||
if (eq) {
|
||||
// everything matches. w00t.
|
||||
foundTags.addAll(tags);
|
||||
if (tags != null)
|
||||
foundTags.addAll(tags);
|
||||
if (newKey != null) foundKey.setData(newKey.getData());
|
||||
return unencrData;
|
||||
}
|
||||
@ -610,4 +612,4 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ public class ElGamalEngine {
|
||||
(ybytes.length > 257 ? 257 : ybytes.length));
|
||||
System.arraycopy(dbytes, 0, out, (dbytes.length < 257 ? 514 - dbytes.length : 257),
|
||||
(dbytes.length > 257 ? 257 : dbytes.length));
|
||||
/*
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
buf.append("Timing\n");
|
||||
buf.append("0-1: ").append(t1 - t0).append('\n');
|
||||
@ -142,6 +143,7 @@ public class ElGamalEngine {
|
||||
buf.append("8-9: ").append(t9 - t8).append('\n');
|
||||
buf.append("9-10: ").append(t10 - t9).append('\n');
|
||||
//_log.debug(buf.toString());
|
||||
*/
|
||||
long end = _context.clock().now();
|
||||
|
||||
long diff = end - start;
|
||||
|
@ -151,15 +151,17 @@ public class I2CPMessageReader {
|
||||
_log.debug("After handling the newly received message");
|
||||
}
|
||||
} catch (I2CPMessageException ime) {
|
||||
_log.error("Error handling message", ime);
|
||||
_log.warn("Error handling message", ime);
|
||||
_listener.readError(I2CPMessageReader.this, ime);
|
||||
cancelRunner();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IO Error handling message", ioe);
|
||||
_log.warn("IO Error handling message", ioe);
|
||||
_listener.disconnected(I2CPMessageReader.this);
|
||||
cancelRunner();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Unhandled error reading I2CP stream", t);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
throw oom;
|
||||
} catch (Exception e) {
|
||||
_log.log(Log.CRIT, "Unhandled error reading I2CP stream", e);
|
||||
_listener.disconnected(I2CPMessageReader.this);
|
||||
cancelRunner();
|
||||
}
|
||||
|
@ -45,6 +45,10 @@ public class SimpleTimer {
|
||||
}
|
||||
}
|
||||
|
||||
public void reschedule(TimedEvent event, long timeoutMs) {
|
||||
addEvent(event, timeoutMs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue up the given event to be fired no sooner than timeoutMs from now.
|
||||
* However, if this event is already scheduled, the event will be scheduled
|
||||
@ -52,7 +56,12 @@ public class SimpleTimer {
|
||||
* timeout. If this is not the desired behavior, call removeEvent first.
|
||||
*
|
||||
*/
|
||||
public void addEvent(TimedEvent event, long timeoutMs) {
|
||||
public void addEvent(TimedEvent event, long timeoutMs) { addEvent(event, timeoutMs, true); }
|
||||
/**
|
||||
* @param useEarliestEventTime if its already scheduled, use the earlier of the
|
||||
* two timeouts, else use the later
|
||||
*/
|
||||
public void addEvent(TimedEvent event, long timeoutMs, boolean useEarliestTime) {
|
||||
int totalEvents = 0;
|
||||
long now = System.currentTimeMillis();
|
||||
long eventTime = now + timeoutMs;
|
||||
@ -61,11 +70,20 @@ public class SimpleTimer {
|
||||
// remove the old scheduled position, then reinsert it
|
||||
Long oldTime = (Long)_eventTimes.get(event);
|
||||
if (oldTime != null) {
|
||||
if (oldTime.longValue() < eventTime) {
|
||||
_events.notifyAll();
|
||||
return; // already scheduled for sooner than requested
|
||||
if (useEarliestTime) {
|
||||
if (oldTime.longValue() < eventTime) {
|
||||
_events.notifyAll();
|
||||
return; // already scheduled for sooner than requested
|
||||
} else {
|
||||
_events.remove(oldTime);
|
||||
}
|
||||
} else {
|
||||
_events.remove(oldTime);
|
||||
if (oldTime.longValue() > eventTime) {
|
||||
_events.notifyAll();
|
||||
return; // already scheduled for later than the given period
|
||||
} else {
|
||||
_events.remove(oldTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (_events.containsKey(time))
|
||||
|
@ -30,10 +30,19 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
_data = null;
|
||||
}
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public void setData(byte data[]) { _data = data; }
|
||||
public byte[] getData() {
|
||||
verifyUnwritten();
|
||||
return _data;
|
||||
}
|
||||
public void setData(byte[] data) {
|
||||
verifyUnwritten();
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public int getSize() { return _data.length; }
|
||||
public int getSize() {
|
||||
verifyUnwritten();
|
||||
return _data.length;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
@ -55,6 +64,7 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
}
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) {
|
||||
verifyUnwritten();
|
||||
if (_data == null) {
|
||||
out[curIndex++] = 0x0;
|
||||
out[curIndex++] = 0x0;
|
||||
@ -70,6 +80,11 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
protected void written() {
|
||||
super.written();
|
||||
_data = null;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
|
@ -28,8 +28,14 @@ public class GarlicMessage extends I2NPMessageImpl {
|
||||
setData(null);
|
||||
}
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public void setData(byte[] data) { _data = data; }
|
||||
public byte[] getData() {
|
||||
verifyUnwritten();
|
||||
return _data;
|
||||
}
|
||||
public void setData(byte[] data) {
|
||||
verifyUnwritten();
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
@ -43,11 +49,13 @@ public class GarlicMessage extends I2NPMessageImpl {
|
||||
}
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
protected int calculateWrittenLength() {
|
||||
protected int calculateWrittenLength() {
|
||||
verifyUnwritten();
|
||||
return 4 + _data.length;
|
||||
}
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
|
||||
verifyUnwritten();
|
||||
byte len[] = DataHelper.toLong(4, _data.length);
|
||||
System.arraycopy(len, 0, out, curIndex, 4);
|
||||
curIndex += 4;
|
||||
@ -62,6 +70,11 @@ public class GarlicMessage extends I2NPMessageImpl {
|
||||
return DataHelper.hashCode(getData());
|
||||
}
|
||||
|
||||
protected void written() {
|
||||
super.written();
|
||||
_data = null;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof GarlicMessage) ) {
|
||||
GarlicMessage msg = (GarlicMessage)object;
|
||||
|
@ -32,7 +32,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
protected I2PAppContext _context;
|
||||
private long _expiration;
|
||||
private long _uniqueId;
|
||||
private byte _data[];
|
||||
private boolean _written;
|
||||
|
||||
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
|
||||
public final static int CHECKSUM_LENGTH = 1; //Hash.HASH_LENGTH;
|
||||
@ -53,6 +53,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
_log = context.logManager().getLog(I2NPMessageImpl.class);
|
||||
_expiration = _context.clock().now() + DEFAULT_EXPIRATION_MS;
|
||||
_uniqueId = _context.random().nextLong(MAX_ID_VALUE);
|
||||
_written = false;
|
||||
//_context.statManager().createRateStat("i2np.writeTime", "How long it takes to write an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
|
||||
//_context.statManager().createRateStat("i2np.readTime", "How long it takes to read an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
@ -264,6 +265,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
|
||||
|
||||
public int toRawByteArray(byte buffer[]) {
|
||||
verifyUnwritten();
|
||||
if (RAW_FULL_SIZE)
|
||||
return toByteArray(buffer);
|
||||
try {
|
||||
@ -277,6 +279,8 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
_context.logManager().getLog(getClass()).log(Log.CRIT, "Error writing", ime);
|
||||
throw new IllegalStateException("Unable to serialize the message (" + getClass().getName()
|
||||
+ "): " + ime.getMessage());
|
||||
} finally {
|
||||
written();
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +320,8 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
}
|
||||
}
|
||||
|
||||
protected void verifyUnwritten() { if (_written) throw new RuntimeException("Already written"); }
|
||||
protected void written() { _written = true; }
|
||||
|
||||
/**
|
||||
* Yes, this is fairly ugly, but its the only place it ever happens.
|
||||
|
@ -66,7 +66,7 @@ public class JobTiming implements Clock.ClockUpdateListener {
|
||||
*/
|
||||
public void end() {
|
||||
_actualEnd = _context.clock().now();
|
||||
_context.clock().removeUpdateListener(this);
|
||||
//_context.clock().removeUpdateListener(this);
|
||||
}
|
||||
|
||||
public void offsetChanged(long delta) {
|
||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.349 $ $Date: 2006/02/18 22:22:32 $";
|
||||
public final static String ID = "$Revision: 1.350 $ $Date: 2006/02/19 07:29:59 $";
|
||||
public final static String VERSION = "0.6.1.10";
|
||||
public final static long BUILD = 5;
|
||||
public final static long BUILD = 6;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
@ -21,6 +21,7 @@ import java.util.Set;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.Payload;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.i2cp.DisconnectMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageException;
|
||||
@ -71,6 +72,7 @@ public class ClientConnectionRunner {
|
||||
*/
|
||||
private List _alreadyProcessed;
|
||||
private ClientWriterRunner _writer;
|
||||
private Hash _destHashCache;
|
||||
/** are we, uh, dead */
|
||||
private boolean _dead;
|
||||
|
||||
@ -144,6 +146,7 @@ public class ClientConnectionRunner {
|
||||
/** currently allocated leaseSet */
|
||||
public LeaseSet getLeaseSet() { return _currentLeaseSet; }
|
||||
void setLeaseSet(LeaseSet ls) { _currentLeaseSet = ls; }
|
||||
public Hash getDestHash() { return _destHashCache; }
|
||||
|
||||
/** current client's sessionId */
|
||||
SessionId getSessionId() { return _sessionId; }
|
||||
@ -206,8 +209,9 @@ public class ClientConnectionRunner {
|
||||
}
|
||||
|
||||
void sessionEstablished(SessionConfig config) {
|
||||
_destHashCache = config.getDestination().calculateHash();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SessionEstablished called for destination " + config.getDestination().calculateHash().toBase64());
|
||||
_log.debug("SessionEstablished called for destination " + _destHashCache.toBase64());
|
||||
_config = config;
|
||||
_manager.destinationEstablished(this);
|
||||
}
|
||||
|
@ -251,21 +251,11 @@ public class ClientManager {
|
||||
}
|
||||
public boolean isLocal(Hash destHash) {
|
||||
if (destHash == null) return false;
|
||||
Set dests = new HashSet();
|
||||
long beforeLock = _ctx.clock().now();
|
||||
long inLock = 0;
|
||||
synchronized (_runners) {
|
||||
inLock = _ctx.clock().now();
|
||||
dests.addAll(_runners.keySet());
|
||||
}
|
||||
long afterLock = _ctx.clock().now();
|
||||
if (afterLock - beforeLock > 50) {
|
||||
_log.warn("isLocal(Hash).locking took too long: " + (afterLock-beforeLock)
|
||||
+ " overall, synchronized took " + (inLock - beforeLock));
|
||||
}
|
||||
for (Iterator iter = dests.iterator(); iter.hasNext();) {
|
||||
Destination d = (Destination)iter.next();
|
||||
if (d.calculateHash().equals(destHash)) return true;
|
||||
for (Iterator iter = _runners.values().iterator(); iter.hasNext(); ) {
|
||||
ClientConnectionRunner cur = (ClientConnectionRunner)iter.next();
|
||||
if (destHash.equals(cur.getDestHash())) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -324,23 +314,12 @@ public class ClientManager {
|
||||
private ClientConnectionRunner getRunner(Hash destHash) {
|
||||
if (destHash == null)
|
||||
return null;
|
||||
Set dests = new HashSet();
|
||||
long beforeLock = _ctx.clock().now();
|
||||
long inLock = 0;
|
||||
synchronized (_runners) {
|
||||
inLock = _ctx.clock().now();
|
||||
dests.addAll(_runners.keySet());
|
||||
}
|
||||
long afterLock = _ctx.clock().now();
|
||||
if (afterLock - beforeLock > 50) {
|
||||
_log.warn("getRunner(Hash).locking took too long: " + (afterLock-beforeLock)
|
||||
+ " overall, synchronized took " + (inLock - beforeLock));
|
||||
}
|
||||
|
||||
for (Iterator iter = dests.iterator(); iter.hasNext(); ) {
|
||||
Destination d = (Destination)iter.next();
|
||||
if (d.calculateHash().equals(destHash))
|
||||
return getRunner(d);
|
||||
for (Iterator iter = _runners.values().iterator(); iter.hasNext(); ) {
|
||||
ClientConnectionRunner cur = (ClientConnectionRunner)iter.next();
|
||||
if (cur.getDestHash().equals(destHash))
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.router.message.SendMessageDirectJob;
|
||||
|
||||
/**
|
||||
@ -41,6 +42,10 @@ class HarvesterJob extends JobImpl {
|
||||
private static final int PRIORITY = 100;
|
||||
|
||||
private static final String PROP_ENABLED = "netDb.shouldHarvest";
|
||||
|
||||
private boolean harvestDirectly() {
|
||||
return Boolean.valueOf(getContext().getProperty("netDb.harvestDirectly", "false")).booleanValue();
|
||||
}
|
||||
|
||||
public HarvesterJob(RouterContext context, KademliaNetworkDatabaseFacade facade) {
|
||||
super(context);
|
||||
@ -107,13 +112,28 @@ class HarvesterJob extends JobImpl {
|
||||
*/
|
||||
private void harvest(Hash peer) {
|
||||
long now = getContext().clock().now();
|
||||
DatabaseLookupMessage msg = new DatabaseLookupMessage(getContext(), true);
|
||||
msg.setFrom(getContext().routerHash());
|
||||
msg.setMessageExpiration(10*1000+now);
|
||||
msg.setSearchKey(peer);
|
||||
msg.setReplyTunnel(null);
|
||||
SendMessageDirectJob job = new SendMessageDirectJob(getContext(), msg, peer, 10*1000, PRIORITY);
|
||||
job.runJob();
|
||||
//getContext().jobQueue().addJob(job);
|
||||
if (harvestDirectly()) {
|
||||
DatabaseLookupMessage msg = new DatabaseLookupMessage(getContext(), true);
|
||||
msg.setFrom(getContext().routerHash());
|
||||
msg.setMessageExpiration(10*1000+now);
|
||||
msg.setSearchKey(peer);
|
||||
msg.setReplyTunnel(null);
|
||||
SendMessageDirectJob job = new SendMessageDirectJob(getContext(), msg, peer, 10*1000, PRIORITY);
|
||||
job.runJob();
|
||||
//getContext().jobQueue().addJob(job);
|
||||
} else {
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel();
|
||||
TunnelInfo sendTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
||||
if ( (replyTunnel != null) && (sendTunnel != null) ) {
|
||||
DatabaseLookupMessage msg = new DatabaseLookupMessage(getContext(), true);
|
||||
msg.setFrom(replyTunnel.getPeer(0));
|
||||
msg.setMessageExpiration(10*1000+now);
|
||||
msg.setSearchKey(peer);
|
||||
msg.setReplyTunnel(replyTunnel.getReceiveTunnelId(0));
|
||||
// we don't even bother to register a reply selector, because we don't really care.
|
||||
// just send it out, and if we get a reply, neat. if not, oh well
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, sendTunnel.getSendTunnelId(0), peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +119,8 @@ public class FIFOBandwidthLimiter {
|
||||
_refiller.reinitialize();
|
||||
}
|
||||
|
||||
public Request createRequest() { return new SimpleRequest(); }
|
||||
|
||||
/**
|
||||
* Request some bytes, blocking until they become available
|
||||
*
|
||||
@ -130,15 +132,21 @@ public class FIFOBandwidthLimiter {
|
||||
}
|
||||
|
||||
SimpleRequest req = new SimpleRequest(bytesIn, 0, purpose);
|
||||
requestInbound(req, bytesIn, purpose);
|
||||
return req;
|
||||
}
|
||||
public void requestInbound(Request req, int bytesIn, String purpose) {
|
||||
req.init(bytesIn, 0, purpose);
|
||||
if (false) { ((SimpleRequest)req).allocateAll(); return; }
|
||||
int pending = 0;
|
||||
synchronized (_pendingInboundRequests) {
|
||||
pending = _pendingInboundRequests.size();
|
||||
_pendingInboundRequests.add(req);
|
||||
}
|
||||
satisfyInboundRequests();
|
||||
satisfyInboundRequests(((SimpleRequest)req).satisfiedBuffer);
|
||||
((SimpleRequest)req).satisfiedBuffer.clear();
|
||||
if (pending > 0)
|
||||
_context.statManager().addRateData("bwLimiter.pendingInboundRequests", pending, pending);
|
||||
return req;
|
||||
}
|
||||
/**
|
||||
* Request some bytes, blocking until they become available
|
||||
@ -151,15 +159,21 @@ public class FIFOBandwidthLimiter {
|
||||
}
|
||||
|
||||
SimpleRequest req = new SimpleRequest(0, bytesOut, purpose);
|
||||
requestOutbound(req, bytesOut, purpose);
|
||||
return req;
|
||||
}
|
||||
public void requestOutbound(Request req, int bytesOut, String purpose) {
|
||||
req.init(0, bytesOut, purpose);
|
||||
if (false) { ((SimpleRequest)req).allocateAll(); return; }
|
||||
int pending = 0;
|
||||
synchronized (_pendingOutboundRequests) {
|
||||
pending = _pendingOutboundRequests.size();
|
||||
_pendingOutboundRequests.add(req);
|
||||
}
|
||||
satisfyOutboundRequests();
|
||||
satisfyOutboundRequests(((SimpleRequest)req).satisfiedBuffer);
|
||||
((SimpleRequest)req).satisfiedBuffer.clear();
|
||||
if (pending > 0)
|
||||
_context.statManager().addRateData("bwLimiter.pendingOutboundRequests", pending, pending);
|
||||
return req;
|
||||
}
|
||||
|
||||
void setInboundBurstKBps(int kbytesPerSecond) {
|
||||
@ -189,7 +203,7 @@ public class FIFOBandwidthLimiter {
|
||||
* @param maxBurstIn allow up to this many bytes in from the burst section for this time period (may be negative)
|
||||
* @param maxBurstOut allow up to this many bytes in from the burst section for this time period (may be negative)
|
||||
*/
|
||||
final void refillBandwidthQueues(long bytesInbound, long bytesOutbound, long maxBurstIn, long maxBurstOut) {
|
||||
final void refillBandwidthQueues(List buf, long bytesInbound, long bytesOutbound, long maxBurstIn, long maxBurstOut) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Refilling the queues with " + bytesInbound + "/" + bytesOutbound + ": " + getStatus().toString());
|
||||
_availableInbound += bytesInbound;
|
||||
@ -251,7 +265,7 @@ public class FIFOBandwidthLimiter {
|
||||
}
|
||||
}
|
||||
|
||||
satisfyRequests();
|
||||
satisfyRequests(buf);
|
||||
updateStats();
|
||||
}
|
||||
|
||||
@ -292,19 +306,20 @@ public class FIFOBandwidthLimiter {
|
||||
* Go through the queue, satisfying as many requests as possible (notifying
|
||||
* each one satisfied that the request has been granted).
|
||||
*/
|
||||
private final void satisfyRequests() {
|
||||
satisfyInboundRequests();
|
||||
satisfyOutboundRequests();
|
||||
private final void satisfyRequests(List buffer) {
|
||||
buffer.clear();
|
||||
satisfyInboundRequests(buffer);
|
||||
buffer.clear();
|
||||
satisfyOutboundRequests(buffer);
|
||||
}
|
||||
|
||||
private final void satisfyInboundRequests() {
|
||||
List satisfied = null;
|
||||
private final void satisfyInboundRequests(List satisfied) {
|
||||
synchronized (_pendingInboundRequests) {
|
||||
if (_inboundUnlimited) {
|
||||
satisfied = locked_satisfyInboundUnlimited();
|
||||
locked_satisfyInboundUnlimited(satisfied);
|
||||
} else {
|
||||
if (_availableInbound > 0) {
|
||||
satisfied = locked_satisfyInboundAvailable();
|
||||
locked_satisfyInboundAvailable(satisfied);
|
||||
} else {
|
||||
// no bandwidth available
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -317,8 +332,8 @@ public class FIFOBandwidthLimiter {
|
||||
|
||||
if (satisfied != null) {
|
||||
for (int i = 0; i < satisfied.size(); i++) {
|
||||
SimpleRequest req = (SimpleRequest)satisfied.get(i);
|
||||
req.notifyAllocation();
|
||||
SimpleRequest creq = (SimpleRequest)satisfied.get(i);
|
||||
creq.notifyAllocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,16 +368,12 @@ public class FIFOBandwidthLimiter {
|
||||
* There are no limits, so just give every inbound request whatever they want
|
||||
*
|
||||
*/
|
||||
private final List locked_satisfyInboundUnlimited() {
|
||||
List satisfied = null;
|
||||
|
||||
private final void locked_satisfyInboundUnlimited(List satisfied) {
|
||||
while (_pendingInboundRequests.size() > 0) {
|
||||
SimpleRequest req = (SimpleRequest)_pendingInboundRequests.remove(0);
|
||||
int allocated = req.getPendingInboundRequested();
|
||||
_totalAllocatedInboundBytes += allocated;
|
||||
req.allocateBytes(allocated, 0);
|
||||
if (satisfied == null)
|
||||
satisfied = new ArrayList(2);
|
||||
satisfied.add(req);
|
||||
long waited = now() - req.getRequestTime();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -373,7 +384,6 @@ public class FIFOBandwidthLimiter {
|
||||
if (waited > 10)
|
||||
_context.statManager().addRateData("bwLimiter.inboundDelayedTime", waited, waited);
|
||||
}
|
||||
return satisfied;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,9 +393,7 @@ public class FIFOBandwidthLimiter {
|
||||
*
|
||||
* @return list of requests that were completely satisfied
|
||||
*/
|
||||
private final List locked_satisfyInboundAvailable() {
|
||||
List satisfied = null;
|
||||
|
||||
private final void locked_satisfyInboundAvailable(List satisfied) {
|
||||
for (int i = 0; i < _pendingInboundRequests.size(); i++) {
|
||||
if (_availableInbound <= 0) break;
|
||||
SimpleRequest req = (SimpleRequest)_pendingInboundRequests.get(i);
|
||||
@ -418,8 +426,6 @@ public class FIFOBandwidthLimiter {
|
||||
_availableInbound -= allocated;
|
||||
_totalAllocatedInboundBytes += allocated;
|
||||
req.allocateBytes(allocated, 0);
|
||||
if (satisfied == null)
|
||||
satisfied = new ArrayList(2);
|
||||
satisfied.add(req);
|
||||
if (req.getPendingInboundRequested() > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -443,17 +449,15 @@ public class FIFOBandwidthLimiter {
|
||||
_context.statManager().addRateData("bwLimiter.inboundDelayedTime", waited, waited);
|
||||
}
|
||||
}
|
||||
return satisfied;
|
||||
}
|
||||
|
||||
private final void satisfyOutboundRequests() {
|
||||
List satisfied = null;
|
||||
private final void satisfyOutboundRequests(List satisfied) {
|
||||
synchronized (_pendingOutboundRequests) {
|
||||
if (_outboundUnlimited) {
|
||||
satisfied = locked_satisfyOutboundUnlimited();
|
||||
locked_satisfyOutboundUnlimited(satisfied);
|
||||
} else {
|
||||
if (_availableOutbound > 0) {
|
||||
satisfied = locked_satisfyOutboundAvailable();
|
||||
locked_satisfyOutboundAvailable(satisfied);
|
||||
} else {
|
||||
// no bandwidth available
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -466,8 +470,8 @@ public class FIFOBandwidthLimiter {
|
||||
|
||||
if (satisfied != null) {
|
||||
for (int i = 0; i < satisfied.size(); i++) {
|
||||
SimpleRequest req = (SimpleRequest)satisfied.get(i);
|
||||
req.notifyAllocation();
|
||||
SimpleRequest creq = (SimpleRequest)satisfied.get(i);
|
||||
creq.notifyAllocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -476,16 +480,12 @@ public class FIFOBandwidthLimiter {
|
||||
* There are no limits, so just give every outbound request whatever they want
|
||||
*
|
||||
*/
|
||||
private final List locked_satisfyOutboundUnlimited() {
|
||||
List satisfied = null;
|
||||
|
||||
private final void locked_satisfyOutboundUnlimited(List satisfied) {
|
||||
while (_pendingOutboundRequests.size() > 0) {
|
||||
SimpleRequest req = (SimpleRequest)_pendingOutboundRequests.remove(0);
|
||||
int allocated = req.getPendingOutboundRequested();
|
||||
_totalAllocatedOutboundBytes += allocated;
|
||||
req.allocateBytes(0, allocated);
|
||||
if (satisfied == null)
|
||||
satisfied = new ArrayList(2);
|
||||
satisfied.add(req);
|
||||
long waited = now() - req.getRequestTime();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -497,7 +497,6 @@ public class FIFOBandwidthLimiter {
|
||||
if (waited > 10)
|
||||
_context.statManager().addRateData("bwLimiter.outboundDelayedTime", waited, waited);
|
||||
}
|
||||
return satisfied;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -507,9 +506,7 @@ public class FIFOBandwidthLimiter {
|
||||
*
|
||||
* @return list of requests that were completely satisfied
|
||||
*/
|
||||
private final List locked_satisfyOutboundAvailable() {
|
||||
List satisfied = null;
|
||||
|
||||
private final void locked_satisfyOutboundAvailable(List satisfied) {
|
||||
for (int i = 0; i < _pendingOutboundRequests.size(); i++) {
|
||||
if (_availableOutbound <= 0) break;
|
||||
SimpleRequest req = (SimpleRequest)_pendingOutboundRequests.get(i);
|
||||
@ -542,8 +539,6 @@ public class FIFOBandwidthLimiter {
|
||||
_availableOutbound -= allocated;
|
||||
_totalAllocatedOutboundBytes += allocated;
|
||||
req.allocateBytes(0, allocated);
|
||||
if (satisfied == null)
|
||||
satisfied = new ArrayList(2);
|
||||
satisfied.add(req);
|
||||
if (req.getPendingOutboundRequested() > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -567,7 +562,6 @@ public class FIFOBandwidthLimiter {
|
||||
_context.statManager().addRateData("bwLimiter.outboundDelayedTime", waited, waited);
|
||||
}
|
||||
}
|
||||
return satisfied;
|
||||
}
|
||||
|
||||
public void renderStatusHTML(Writer out) throws IOException {
|
||||
@ -613,14 +607,24 @@ public class FIFOBandwidthLimiter {
|
||||
private String _target;
|
||||
private int _allocationsSinceWait;
|
||||
private boolean _aborted;
|
||||
List satisfiedBuffer;
|
||||
|
||||
public SimpleRequest() {
|
||||
satisfiedBuffer = new ArrayList(1);
|
||||
init(0, 0, null);
|
||||
}
|
||||
public SimpleRequest(int in, int out, String target) {
|
||||
satisfiedBuffer = new ArrayList(1);
|
||||
init(in, out, target);
|
||||
}
|
||||
public void init(int in, int out, String target) {
|
||||
_inTotal = in;
|
||||
_outTotal = out;
|
||||
_inAllocated = 0;
|
||||
_outAllocated = 0;
|
||||
_aborted = false;
|
||||
_target = target;
|
||||
satisfiedBuffer.clear();
|
||||
_requestId = ++__requestId;
|
||||
_requestTime = now();
|
||||
}
|
||||
@ -646,6 +650,13 @@ public class FIFOBandwidthLimiter {
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
int getAllocationsSinceWait() { return _allocationsSinceWait; }
|
||||
void allocateAll() {
|
||||
_inAllocated = _inTotal;
|
||||
_outAllocated = _outTotal;
|
||||
_outAllocated = _outTotal;
|
||||
_allocationsSinceWait++;
|
||||
notifyAllocation();
|
||||
}
|
||||
void allocateBytes(int in, int out) {
|
||||
_inAllocated += in;
|
||||
_outAllocated += out;
|
||||
@ -677,7 +688,8 @@ public class FIFOBandwidthLimiter {
|
||||
public void abort();
|
||||
/** was this request aborted? */
|
||||
public boolean getAborted();
|
||||
|
||||
/** thar be dragons */
|
||||
public void init(int in, int out, String target);
|
||||
}
|
||||
|
||||
private static final NoopRequest _noop = new NoopRequest();
|
||||
@ -691,5 +703,6 @@ public class FIFOBandwidthLimiter {
|
||||
public int getTotalInboundRequested() { return 0; }
|
||||
public int getTotalOutboundRequested() { return 0; }
|
||||
public void waitForNextAllocation() {}
|
||||
public void init(int in, int out, String target) {}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.i2p.router.transport;
|
||||
|
||||
import java.util.*;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -62,6 +63,7 @@ class FIFOBandwidthRefiller implements Runnable {
|
||||
public void run() {
|
||||
// bootstrap 'em with nothing
|
||||
_lastRefillTime = _limiter.now();
|
||||
List buffer = new ArrayList(2);
|
||||
while (true) {
|
||||
long now = _limiter.now();
|
||||
if (now >= _lastCheckConfigTime + _configCheckPeriodMs) {
|
||||
@ -70,7 +72,7 @@ class FIFOBandwidthRefiller implements Runnable {
|
||||
_lastCheckConfigTime = now;
|
||||
}
|
||||
|
||||
boolean updated = updateQueues(now);
|
||||
boolean updated = updateQueues(buffer, now);
|
||||
if (updated) {
|
||||
_lastRefillTime = now;
|
||||
}
|
||||
@ -85,7 +87,7 @@ class FIFOBandwidthRefiller implements Runnable {
|
||||
_lastCheckConfigTime = _lastRefillTime;
|
||||
}
|
||||
|
||||
private boolean updateQueues(long now) {
|
||||
private boolean updateQueues(List buffer, long now) {
|
||||
long numMs = (now - _lastRefillTime);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Updating bandwidth after " + numMs + " (status: " + _limiter.getStatus().toString()
|
||||
@ -114,7 +116,7 @@ class FIFOBandwidthRefiller implements Runnable {
|
||||
|
||||
long maxBurstIn = ((_inboundBurstKBytesPerSecond-_inboundKBytesPerSecond)*1024*numMs)/1000;
|
||||
long maxBurstOut = ((_outboundBurstKBytesPerSecond-_outboundKBytesPerSecond)*1024*numMs)/1000;
|
||||
_limiter.refillBandwidthQueues(inboundToAdd, outboundToAdd, maxBurstIn, maxBurstOut);
|
||||
_limiter.refillBandwidthQueues(buffer, inboundToAdd, outboundToAdd, maxBurstIn, maxBurstOut);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Adding " + inboundToAdd + " bytes to inboundAvailable");
|
||||
@ -331,4 +333,4 @@ class FIFOBandwidthRefiller implements Runnable {
|
||||
|
||||
int getOutboundKBytesPerSecond() { return _outboundKBytesPerSecond; }
|
||||
int getInboundKBytesPerSecond() { return _inboundKBytesPerSecond; }
|
||||
}
|
||||
}
|
||||
|
@ -140,12 +140,12 @@ public abstract class TransportImpl implements Transport {
|
||||
long lifetime = msg.getLifetime();
|
||||
if (lifetime > 3000) {
|
||||
int level = Log.WARN;
|
||||
//if (!sendSuccessful)
|
||||
// level = Log.INFO;
|
||||
if (!sendSuccessful)
|
||||
level = Log.INFO;
|
||||
if (_log.shouldLog(level))
|
||||
_log.log(level, "afterSend: [success=" + sendSuccessful + "]" + msg.getMessageSize() + "byte "
|
||||
_log.log(level, "afterSend slow (" + lifetime + "): [success=" + sendSuccessful + "]" + msg.getMessageSize() + "byte "
|
||||
+ msg.getMessageType() + " " + msg.getMessageId() + " from " + _context.routerHash().toBase64().substring(0,6)
|
||||
+ " to " + msg.getTarget().getIdentity().calculateHash().toBase64().substring(0,6) + "\n" + msg.toString());
|
||||
+ " to " + msg.getTarget().getIdentity().calculateHash().toBase64().substring(0,6) + ": " + msg.toString());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("afterSend: [success=" + sendSuccessful + "]" + msg.getMessageSize() + "byte "
|
||||
|
@ -125,8 +125,6 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
|
||||
boolean fragmentOK = false;
|
||||
boolean partialACK = false;
|
||||
|
||||
// perhaps compact the synchronized block further by synchronizing on the
|
||||
// particular state once its found?
|
||||
synchronized (messages) {
|
||||
state = (InboundMessageState)messages.get(messageId);
|
||||
if (state == null) {
|
||||
@ -145,36 +143,36 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
|
||||
} else {
|
||||
partialACK = true;
|
||||
}
|
||||
|
||||
if (messageComplete) {
|
||||
_recentlyCompletedMessages.add(mid);
|
||||
_messageReceiver.receiveMessage(state);
|
||||
|
||||
from.messageFullyReceived(messageId, state.getCompleteSize());
|
||||
_ackSender.ackPeer(from);
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message received completely! " + state);
|
||||
|
||||
_context.statManager().addRateData("udp.receivedCompleteTime", state.getLifetime(), state.getLifetime());
|
||||
if (state.getFragmentCount() > 0)
|
||||
_context.statManager().addRateData("udp.receivedCompleteFragments", state.getFragmentCount(), state.getLifetime());
|
||||
} else if (messageExpired) {
|
||||
state.releaseResources();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Message expired while only being partially read: " + state);
|
||||
_context.messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "expired hile partially read: " + state.toString());
|
||||
} else if (partialACK) {
|
||||
// not expired but not yet complete... lets queue up a partial ACK
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Queueing up a partial ACK for peer: " + from + " for " + state);
|
||||
from.messagePartiallyReceived();
|
||||
_ackSender.ackPeer(from);
|
||||
}
|
||||
|
||||
if (!fragmentOK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (messageComplete) {
|
||||
_recentlyCompletedMessages.add(mid);
|
||||
_messageReceiver.receiveMessage(state);
|
||||
|
||||
from.messageFullyReceived(messageId, state.getCompleteSize());
|
||||
_ackSender.ackPeer(from);
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message received completely! " + state);
|
||||
|
||||
_context.statManager().addRateData("udp.receivedCompleteTime", state.getLifetime(), state.getLifetime());
|
||||
if (state.getFragmentCount() > 0)
|
||||
_context.statManager().addRateData("udp.receivedCompleteFragments", state.getFragmentCount(), state.getLifetime());
|
||||
} else if (messageExpired) {
|
||||
state.releaseResources();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Message expired while only being partially read: " + state);
|
||||
_context.messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "expired hile partially read: " + state.toString());
|
||||
} else if (partialACK) {
|
||||
// not expired but not yet complete... lets queue up a partial ACK
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Queueing up a partial ACK for peer: " + from + " for " + state);
|
||||
from.messagePartiallyReceived();
|
||||
_ackSender.ackPeer(from);
|
||||
}
|
||||
|
||||
if (!fragmentOK)
|
||||
break;
|
||||
}
|
||||
return fragments;
|
||||
}
|
||||
@ -183,16 +181,17 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
|
||||
int rv = 0;
|
||||
if (data.readACKsIncluded()) {
|
||||
int fragments = 0;
|
||||
long acks[] = data.readACKs();
|
||||
if (acks != null) {
|
||||
rv += acks.length;
|
||||
_context.statManager().addRateData("udp.receivedACKs", acks.length, 0);
|
||||
int ackCount = data.readACKCount();
|
||||
if (ackCount > 0) {
|
||||
rv += ackCount;
|
||||
_context.statManager().addRateData("udp.receivedACKs", ackCount, 0);
|
||||
//_context.statManager().getStatLog().addData(from.getRemoteHostId().toString(), "udp.peer.receiveACKCount", acks.length, 0);
|
||||
|
||||
for (int i = 0; i < acks.length; i++) {
|
||||
for (int i = 0; i < ackCount; i++) {
|
||||
long id = data.readACK(i);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Full ACK of message " + acks[i] + " received!");
|
||||
fragments += _outbound.acked(acks[i], from.getRemotePeer());
|
||||
_log.info("Full ACK of message " + id + " received!");
|
||||
fragments += _outbound.acked(id, from.getRemotePeer());
|
||||
}
|
||||
} else {
|
||||
_log.error("Received ACKs with no acks?! " + data);
|
||||
|
@ -202,14 +202,16 @@ public class OutboundMessageFragments {
|
||||
*/
|
||||
private void finishMessages() {
|
||||
int rv = 0;
|
||||
List peers = new ArrayList();
|
||||
List peers = null;
|
||||
synchronized (_activePeers) {
|
||||
peers = new ArrayList(_activePeers);
|
||||
peers = new ArrayList(_activePeers.size());
|
||||
for (int i = 0; i < _activePeers.size(); i++) {
|
||||
PeerState state = (PeerState)_activePeers.get(i);
|
||||
if (state.getOutboundMessageCount() <= 0) {
|
||||
_activePeers.remove(i);
|
||||
i--;
|
||||
} else {
|
||||
peers.add(state);
|
||||
}
|
||||
}
|
||||
_activePeers.notifyAll();
|
||||
@ -297,11 +299,13 @@ public class OutboundMessageFragments {
|
||||
for (int i = 0; packets != null && i < packets.length ; i++)
|
||||
if (packets[i] != null)
|
||||
valid++;
|
||||
/*
|
||||
state.getMessage().timestamp("sending a volley of " + valid
|
||||
+ " lastReceived: "
|
||||
+ (_context.clock().now() - peer.getLastReceiveTime())
|
||||
+ " lastSentFully: "
|
||||
+ (_context.clock().now() - peer.getLastSendFullyTime()));
|
||||
*/
|
||||
}
|
||||
return packets;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public class PacketBuilder {
|
||||
* included, it should be removed from the list.
|
||||
*/
|
||||
public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List ackIdsRemaining, List partialACKsRemaining) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
|
||||
StringBuffer msg = null;
|
||||
boolean acksIncluded = false;
|
||||
@ -156,8 +156,10 @@ public class PacketBuilder {
|
||||
off++;
|
||||
|
||||
int size = state.fragmentSize(fragment);
|
||||
if (size < 0)
|
||||
if (size < 0) {
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
DataHelper.toLong(data, off, 2, size);
|
||||
data[off] &= (byte)0x3F; // 2 highest bits are reserved
|
||||
off += 2;
|
||||
@ -166,12 +168,16 @@ public class PacketBuilder {
|
||||
if (sizeWritten != size) {
|
||||
_log.error("Size written: " + sizeWritten + " but size: " + size
|
||||
+ " for fragment " + fragment + " of " + state.getMessageId());
|
||||
packet.release();
|
||||
return null;
|
||||
} else if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Size written: " + sizeWritten + " for fragment " + fragment
|
||||
+ " of " + state.getMessageId());
|
||||
size = sizeWritten;
|
||||
if (size < 0) return null;
|
||||
if (size < 0) {
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
off += size;
|
||||
|
||||
// we can pad here if we want, maybe randomized?
|
||||
@ -202,7 +208,7 @@ public class PacketBuilder {
|
||||
* @param ackBitfields list of ACKBitfield instances to either fully or partially ACK
|
||||
*/
|
||||
public UDPPacket buildACK(PeerState peer, List ackBitfields) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
|
||||
StringBuffer msg = null;
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
@ -308,7 +314,7 @@ public class PacketBuilder {
|
||||
* @return ready to send packet, or null if there was a problem
|
||||
*/
|
||||
public UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
|
||||
InetAddress to = null;
|
||||
try {
|
||||
@ -316,6 +322,7 @@ public class PacketBuilder {
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString());
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -337,6 +344,7 @@ public class PacketBuilder {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("How did our sent IP become invalid? " + state);
|
||||
state.fail();
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
// now for the body
|
||||
@ -408,9 +416,10 @@ public class PacketBuilder {
|
||||
* @return ready to send packet, or null if there was a problem
|
||||
*/
|
||||
public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte toIP[] = state.getSentIP();
|
||||
if ( (_transport !=null) && (!_transport.isValid(toIP)) ) {
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
InetAddress to = null;
|
||||
@ -419,6 +428,7 @@ public class PacketBuilder {
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString());
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -488,13 +498,14 @@ public class PacketBuilder {
|
||||
* @return ready to send packets, or null if there was a problem
|
||||
*/
|
||||
public UDPPacket buildSessionConfirmedPacket(OutboundEstablishState state, int fragmentNum, int numFragments, byte identity[]) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
InetAddress to = null;
|
||||
try {
|
||||
to = InetAddress.getByAddress(state.getSentIP());
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString());
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -578,7 +589,7 @@ public class PacketBuilder {
|
||||
return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey);
|
||||
}
|
||||
public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -619,7 +630,7 @@ public class PacketBuilder {
|
||||
* @return ready to send packet, or null if there was a problem
|
||||
*/
|
||||
public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -665,7 +676,7 @@ public class PacketBuilder {
|
||||
public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce,
|
||||
InetAddress charlieIP, int charliePort,
|
||||
SessionKey charlieCipherKey, SessionKey charlieMACKey) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -709,7 +720,7 @@ public class PacketBuilder {
|
||||
* @return ready to send packet, or null if there was a problem
|
||||
*/
|
||||
public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -783,7 +794,7 @@ public class PacketBuilder {
|
||||
}
|
||||
|
||||
public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -851,7 +862,7 @@ public class PacketBuilder {
|
||||
private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4);
|
||||
|
||||
public UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -907,7 +918,7 @@ public class PacketBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -954,7 +965,7 @@ public class PacketBuilder {
|
||||
}
|
||||
|
||||
public UDPPacket buildHolePunch(UDPPacketReader reader) {
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, false);
|
||||
byte data[] = packet.getPacket().getData();
|
||||
Arrays.fill(data, 0, data.length, (byte)0x0);
|
||||
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
||||
@ -970,6 +981,7 @@ public class PacketBuilder {
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("IP for alice to hole punch to is invalid", uhe);
|
||||
packet.release();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -167,10 +167,10 @@ public class PeerState {
|
||||
private long _packetsReceivedDuplicate;
|
||||
private long _packetsReceived;
|
||||
|
||||
/** Message (Long) to InboundMessageState for active message */
|
||||
/** list of InboundMessageState for active message */
|
||||
private Map _inboundMessages;
|
||||
/** Message (Long) to OutboundMessageState */
|
||||
private Map _outboundMessages;
|
||||
/** list of OutboundMessageState */
|
||||
private List _outboundMessages;
|
||||
/** which outbound message is currently being retransmitted */
|
||||
private OutboundMessageState _retransmitter;
|
||||
|
||||
@ -262,7 +262,7 @@ public class PeerState {
|
||||
_packetsReceived = 0;
|
||||
_packetsReceivedDuplicate = 0;
|
||||
_inboundMessages = new HashMap(8);
|
||||
_outboundMessages = new HashMap(8);
|
||||
_outboundMessages = new ArrayList(32);
|
||||
_dead = false;
|
||||
_context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
@ -603,7 +603,8 @@ public class PeerState {
|
||||
int rv = 0;
|
||||
|
||||
synchronized (_inboundMessages) {
|
||||
for (Iterator iter = _inboundMessages.values().iterator(); iter.hasNext(); ) {
|
||||
int remaining = _inboundMessages.size();
|
||||
for (Iterator iter = _inboundMessages.values().iterator(); remaining > 0; remaining--) {
|
||||
InboundMessageState state = (InboundMessageState)iter.next();
|
||||
if (state.isExpired()) {
|
||||
iter.remove();
|
||||
@ -687,7 +688,7 @@ public class PeerState {
|
||||
List rv = null;
|
||||
int bytesRemaining = countMaxACKData();
|
||||
synchronized (_currentACKs) {
|
||||
rv = new ArrayList(_currentACKs.size());
|
||||
rv = new ArrayList(16); //_currentACKs.size());
|
||||
int oldIndex = _currentACKsResend.size();
|
||||
while ( (bytesRemaining >= 4) && (_currentACKs.size() > 0) ) {
|
||||
Long val = (Long)_currentACKs.remove(0);
|
||||
@ -701,7 +702,10 @@ public class PeerState {
|
||||
if (alwaysIncludeRetransmissions || rv.size() > 0) {
|
||||
// now repeat by putting in some old ACKs
|
||||
for (int i = 0; (i < oldIndex) && (bytesRemaining >= 4); i++) {
|
||||
rv.add(new FullACKBitfield(((Long)_currentACKsResend.get(i)).longValue()));
|
||||
Long cur = (Long)_currentACKsResend.get(i);
|
||||
long c = cur.longValue();
|
||||
FullACKBitfield bf = new FullACKBitfield(c);
|
||||
rv.add(bf);
|
||||
bytesRemaining -= 4;
|
||||
}
|
||||
}
|
||||
@ -749,7 +753,9 @@ public class PeerState {
|
||||
int numMessages = _inboundMessages.size();
|
||||
if (numMessages <= 0)
|
||||
return;
|
||||
for (Iterator iter = _inboundMessages.values().iterator(); iter.hasNext(); ) {
|
||||
// todo: make this a list instead of a map, so we can iterate faster w/out the memory overhead?
|
||||
int remaining = _inboundMessages.size();
|
||||
for (Iterator iter = _inboundMessages.values().iterator(); remaining > 0; remaining--) {
|
||||
InboundMessageState state = (InboundMessageState)iter.next();
|
||||
if (state.isExpired()) {
|
||||
//if (_context instanceof RouterContext)
|
||||
@ -974,10 +980,10 @@ public class PeerState {
|
||||
state.setPeer(this);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Adding to " + _remotePeer.toBase64() + ": " + state.getMessageId());
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (msgs == null) return 0;
|
||||
synchronized (msgs) {
|
||||
msgs.put(new Long(state.getMessageId()), state);
|
||||
msgs.add(state);
|
||||
return msgs.size();
|
||||
}
|
||||
}
|
||||
@ -985,20 +991,23 @@ public class PeerState {
|
||||
public void dropOutbound() {
|
||||
if (_dead) return;
|
||||
_dead = true;
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
//_outboundMessages = null;
|
||||
_retransmitter = null;
|
||||
if (msgs != null) {
|
||||
List tempList = null;
|
||||
synchronized (msgs) {
|
||||
for (Iterator iter = msgs.values().iterator(); iter.hasNext();)
|
||||
_transport.failed((OutboundMessageState)iter.next());
|
||||
tempList = new ArrayList(msgs);
|
||||
msgs.clear();
|
||||
}
|
||||
int sz = tempList.size();
|
||||
for (int i = 0; i < sz; i++)
|
||||
_transport.failed((OutboundMessageState)tempList.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public int getOutboundMessageCount() {
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (_dead) return 0;
|
||||
if (msgs != null) {
|
||||
synchronized (msgs) {
|
||||
@ -1015,29 +1024,35 @@ public class PeerState {
|
||||
*/
|
||||
public int finishMessages() {
|
||||
int rv = 0;
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (_dead) return 0;
|
||||
List succeeded = null;
|
||||
List failed = null;
|
||||
synchronized (msgs) {
|
||||
for (Iterator iter = msgs.keySet().iterator(); iter.hasNext(); ) {
|
||||
Long id = (Long)iter.next();
|
||||
OutboundMessageState state = (OutboundMessageState)msgs.get(id);
|
||||
int size = msgs.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OutboundMessageState state = (OutboundMessageState)msgs.get(i);
|
||||
if (state.isComplete()) {
|
||||
iter.remove();
|
||||
msgs.remove(i);
|
||||
i--;
|
||||
size--;
|
||||
if (_retransmitter == state)
|
||||
_retransmitter = null;
|
||||
if (succeeded == null) succeeded = new ArrayList(4);
|
||||
succeeded.add(state);
|
||||
} else if (state.isExpired()) {
|
||||
iter.remove();
|
||||
msgs.remove(i);
|
||||
i--;
|
||||
size--;
|
||||
if (_retransmitter == state)
|
||||
_retransmitter = null;
|
||||
_context.statManager().addRateData("udp.sendFailed", state.getPushCount(), state.getLifetime());
|
||||
if (failed == null) failed = new ArrayList(4);
|
||||
failed.add(state);
|
||||
} else if (state.getPushCount() > OutboundMessageFragments.MAX_VOLLEYS) {
|
||||
iter.remove();
|
||||
msgs.remove(i);
|
||||
i--;
|
||||
size--;
|
||||
if (state == _retransmitter)
|
||||
_retransmitter = null;
|
||||
_context.statManager().addRateData("udp.sendAggressiveFailed", state.getPushCount(), state.getLifetime());
|
||||
@ -1082,11 +1097,12 @@ public class PeerState {
|
||||
*/
|
||||
public OutboundMessageState allocateSend() {
|
||||
int total = 0;
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (_dead) return null;
|
||||
synchronized (msgs) {
|
||||
for (Iterator iter = msgs.values().iterator(); iter.hasNext(); ) {
|
||||
OutboundMessageState state = (OutboundMessageState)iter.next();
|
||||
int size = msgs.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OutboundMessageState state = (OutboundMessageState)msgs.get(i);
|
||||
if (locked_shouldSend(state)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Allocate sending to " + _remotePeer.toBase64() + ": " + state.getMessageId());
|
||||
@ -1099,11 +1115,11 @@ public class PeerState {
|
||||
}
|
||||
*/
|
||||
return state;
|
||||
} else {
|
||||
} /* else {
|
||||
OutNetMessage msg = state.getMessage();
|
||||
if (msg != null)
|
||||
msg.timestamp("passed over for allocation with " + msgs.size() + " peers");
|
||||
}
|
||||
} */
|
||||
}
|
||||
total = msgs.size();
|
||||
}
|
||||
@ -1118,7 +1134,7 @@ public class PeerState {
|
||||
public int getNextDelay() {
|
||||
int rv = -1;
|
||||
long now = _context.clock().now();
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (_dead) return -1;
|
||||
synchronized (msgs) {
|
||||
if (_retransmitter != null) {
|
||||
@ -1128,8 +1144,9 @@ public class PeerState {
|
||||
else
|
||||
return rv;
|
||||
}
|
||||
for (Iterator iter = msgs.values().iterator(); iter.hasNext(); ) {
|
||||
OutboundMessageState state = (OutboundMessageState)iter.next();
|
||||
int size = msgs.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OutboundMessageState state = (OutboundMessageState)msgs.get(i);
|
||||
int delay = (int)(state.getNextSendTime() - now);
|
||||
if (delay <= 0)
|
||||
delay = 1;
|
||||
@ -1140,7 +1157,6 @@ public class PeerState {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If set to true, we should throttle retransmissions of all but the first message in
|
||||
* flight to a peer. If set to false, we will only throttle the initial flight of a
|
||||
@ -1182,17 +1198,18 @@ public class PeerState {
|
||||
if ( (_retransmitter != null) && (_retransmitter != state) ) {
|
||||
// choke it, since there's already another message retransmitting to this
|
||||
// peer.
|
||||
_context.statManager().addRateData("udp.blockedRetransmissions", getPacketsRetransmitted(), getPacketsTransmitted());
|
||||
if ( (state.getMaxSends() <= 0) && (!THROTTLE_INITIAL_SEND) ) {
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("another message is retransmitting, but we want to send our first volley...");
|
||||
} else if ( (state.getMaxSends() <= 0) || (THROTTLE_RESENDS) ) {
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("choked, with another message retransmitting");
|
||||
_context.statManager().addRateData("udp.blockedRetransmissions", _packetsRetransmitted, _packetsTransmitted);
|
||||
int max = state.getMaxSends();
|
||||
if ( (max <= 0) && (!THROTTLE_INITIAL_SEND) ) {
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("another message is retransmitting, but we want to send our first volley...");
|
||||
} else if ( (max <= 0) || (THROTTLE_RESENDS) ) {
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("choked, with another message retransmitting");
|
||||
return false;
|
||||
} else {
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("another message is retransmitting, but since we've already begun sending...");
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("another message is retransmitting, but since we've already begun sending...");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1218,8 +1235,8 @@ public class PeerState {
|
||||
return true;
|
||||
} else {
|
||||
_context.statManager().addRateData("udp.sendRejected", state.getPushCount(), state.getLifetime());
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("send rejected, available=" + getSendWindowBytesRemaining());
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("send rejected, available=" + getSendWindowBytesRemaining());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Allocation of " + size + " rejected w/ wsize=" + getSendWindowBytes()
|
||||
+ " available=" + getSendWindowBytesRemaining()
|
||||
@ -1229,10 +1246,10 @@ public class PeerState {
|
||||
_log.warn("Retransmit after choke for next send time in " + (state.getNextSendTime()-now) + "ms");
|
||||
//_throttle.choke(peer.getRemotePeer());
|
||||
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("choked, not enough available, wsize="
|
||||
+ getSendWindowBytes() + " available="
|
||||
+ getSendWindowBytesRemaining());
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("choked, not enough available, wsize="
|
||||
// + getSendWindowBytes() + " available="
|
||||
// + getSendWindowBytesRemaining());
|
||||
return false;
|
||||
}
|
||||
} // nextTime <= now
|
||||
@ -1242,23 +1259,32 @@ public class PeerState {
|
||||
|
||||
public int acked(long messageId) {
|
||||
OutboundMessageState state = null;
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
if (_dead) return 0;
|
||||
synchronized (msgs) {
|
||||
state = (OutboundMessageState)msgs.remove(new Long(messageId));
|
||||
int sz = msgs.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
state = (OutboundMessageState)msgs.get(i);
|
||||
if (state.getMessageId() == messageId) {
|
||||
msgs.remove(i);
|
||||
break;
|
||||
} else {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
if ( (state != null) && (state == _retransmitter) )
|
||||
_retransmitter = null;
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
int numSends = state.getMaxSends();
|
||||
if (state.getMessage() != null) {
|
||||
state.getMessage().timestamp("acked after " + numSends
|
||||
+ " lastReceived: "
|
||||
+ (_context.clock().now() - getLastReceiveTime())
|
||||
+ " lastSentFully: "
|
||||
+ (_context.clock().now() - getLastSendFullyTime()));
|
||||
}
|
||||
//if (state.getMessage() != null) {
|
||||
// state.getMessage().timestamp("acked after " + numSends
|
||||
// + " lastReceived: "
|
||||
// + (_context.clock().now() - getLastReceiveTime())
|
||||
// + " lastSentFully: "
|
||||
// + (_context.clock().now() - getLastSendFullyTime()));
|
||||
//}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Received ack of " + messageId + " by " + _remotePeer.toBase64()
|
||||
@ -1294,19 +1320,24 @@ public class PeerState {
|
||||
return;
|
||||
}
|
||||
|
||||
Map msgs = _outboundMessages;
|
||||
List msgs = _outboundMessages;
|
||||
|
||||
OutboundMessageState state = null;
|
||||
boolean isComplete = false;
|
||||
synchronized (msgs) {
|
||||
state = (OutboundMessageState)msgs.get(new Long(bitfield.getMessageId()));
|
||||
if (state != null) {
|
||||
if (state.acked(bitfield)) {
|
||||
// this partial ack actually clears it fully
|
||||
isComplete = true;
|
||||
msgs.remove(new Long(bitfield.getMessageId()));
|
||||
if (state == _retransmitter)
|
||||
_retransmitter = null;
|
||||
for (int i = 0; i < msgs.size(); i++) {
|
||||
state = (OutboundMessageState)msgs.get(i);
|
||||
if (state.getMessageId() == bitfield.getMessageId()) {
|
||||
boolean complete = state.acked(bitfield);
|
||||
if (complete) {
|
||||
isComplete = true;
|
||||
msgs.remove(i);
|
||||
if (state == _retransmitter)
|
||||
_retransmitter = null;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1333,8 +1364,8 @@ public class PeerState {
|
||||
_context.statManager().addRateData("udp.sendConfirmFragments", state.getFragmentCount(), state.getLifetime());
|
||||
if (numSends > 1)
|
||||
_context.statManager().addRateData("udp.sendConfirmVolley", numSends, state.getFragmentCount());
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("partial ack to complete after " + numSends);
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("partial ack to complete after " + numSends);
|
||||
_transport.succeeded(state);
|
||||
|
||||
// this adjusts the rtt/rto/window/etc
|
||||
@ -1344,8 +1375,8 @@ public class PeerState {
|
||||
|
||||
state.releaseResources();
|
||||
} else {
|
||||
if (state.getMessage() != null)
|
||||
state.getMessage().timestamp("partial ack after " + numSends + ": " + bitfield.toString());
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("partial ack after " + numSends + ": " + bitfield.toString());
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
@ -1392,21 +1423,16 @@ public class PeerState {
|
||||
msgs.clear();
|
||||
|
||||
OutboundMessageState retransmitter = null;
|
||||
Map omsgs = oldPeer._outboundMessages;
|
||||
if (omsgs != null) {
|
||||
synchronized (omsgs) {
|
||||
msgs.putAll(omsgs);
|
||||
omsgs.clear();
|
||||
retransmitter = oldPeer._retransmitter;
|
||||
}
|
||||
synchronized (oldPeer._outboundMessages) {
|
||||
tmp.addAll(oldPeer._outboundMessages);
|
||||
oldPeer._outboundMessages.clear();
|
||||
retransmitter = oldPeer._retransmitter;
|
||||
}
|
||||
omsgs = _outboundMessages;
|
||||
if (omsgs != null) {
|
||||
synchronized (omsgs) {
|
||||
omsgs.putAll(msgs);
|
||||
_retransmitter = retransmitter;
|
||||
}
|
||||
synchronized (_outboundMessages) {
|
||||
_outboundMessages.addAll(tmp);
|
||||
_retransmitter = retransmitter;
|
||||
}
|
||||
tmp.clear();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
|
@ -108,9 +108,10 @@ public class UDPEndpointTest {
|
||||
curPeer = 0;
|
||||
short priority = 1;
|
||||
long expiration = -1;
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
try {
|
||||
packet.initialize(priority, expiration, InetAddress.getLocalHost(), _endpoints[curPeer].getListenPort());
|
||||
UDPPacket packet = UDPPacket.acquire(_context, true);
|
||||
//try {
|
||||
if (true) throw new RuntimeException("fixme");
|
||||
//packet.initialize(priority, expiration, InetAddress.getLocalHost(), _endpoints[curPeer].getListenPort());
|
||||
packet.writeData(data, 0, 1024);
|
||||
packet.getPacket().setLength(1024);
|
||||
int outstanding = _sentNotReceived.size() + 1;
|
||||
@ -118,9 +119,9 @@ public class UDPEndpointTest {
|
||||
_log.debug("Sending packet " + curPacket + " with outstanding " + outstanding);
|
||||
_endpoint.send(packet);
|
||||
//try { Thread.sleep(10); } catch (InterruptedException ie) {}
|
||||
} catch (UnknownHostException uhe) {
|
||||
_log.error("foo!", uhe);
|
||||
}
|
||||
//} catch (UnknownHostException uhe) {
|
||||
// _log.error("foo!", uhe);
|
||||
//}
|
||||
//if (_log.shouldLog(Log.DEBUG)) {
|
||||
// _log.debug("Sent to " + _endpoints[curPeer].getListenPort() + " from " + _endpoint.getListenPort());
|
||||
//}
|
||||
|
@ -40,6 +40,7 @@ public class UDPPacket {
|
||||
private long _receivedTime;
|
||||
private long _beforeReceiveFragments;
|
||||
private long _afterHandlingTime;
|
||||
private boolean _isInbound;
|
||||
|
||||
private static final List _packetCache;
|
||||
static {
|
||||
@ -47,7 +48,8 @@ public class UDPPacket {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(UDPPacket.class);
|
||||
}
|
||||
|
||||
private static final boolean CACHE = false; // TODO: support caching to cut churn down a /lot/
|
||||
private static final boolean CACHE = true; // TODO: support caching to cut churn down a /lot/
|
||||
private static final int CACHE_SIZE = 64;
|
||||
|
||||
static final int MAX_PACKET_SIZE = 2048;
|
||||
public static final int IV_SIZE = 16;
|
||||
@ -75,18 +77,31 @@ public class UDPPacket {
|
||||
private static final int MAX_VALIDATE_SIZE = MAX_PACKET_SIZE;
|
||||
private static final ByteCache _validateCache = ByteCache.getInstance(64, MAX_VALIDATE_SIZE);
|
||||
private static final ByteCache _ivCache = ByteCache.getInstance(64, IV_SIZE);
|
||||
private static final ByteCache _dataCache = ByteCache.getInstance(128, MAX_PACKET_SIZE);
|
||||
private static final ByteCache _dataCache = ByteCache.getInstance(64, MAX_PACKET_SIZE);
|
||||
|
||||
private UDPPacket(I2PAppContext ctx) {
|
||||
private UDPPacket(I2PAppContext ctx, boolean inbound) {
|
||||
ctx.statManager().createRateStat("udp.packetsLiveInbound", "Number of live inbound packets in memory", "udp", new long[] { 60*1000, 5*60*1000 });
|
||||
ctx.statManager().createRateStat("udp.packetsLiveOutbound", "Number of live outbound packets in memory", "udp", new long[] { 60*1000, 5*60*1000 });
|
||||
ctx.statManager().createRateStat("udp.packetsLivePendingRecvInbound", "Number of live inbound packets not yet handled by the PacketHandler", "udp", new long[] { 60*1000, 5*60*1000 });
|
||||
ctx.statManager().createRateStat("udp.packetsLivePendingHandleInbound", "Number of live inbound packets not yet handled fully by the PacketHandler", "udp", new long[] { 60*1000, 5*60*1000 });
|
||||
// the data buffer is clobbered on init(..), but we need it to bootstrap
|
||||
_packet = new DatagramPacket(new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
|
||||
init(ctx, inbound);
|
||||
}
|
||||
private void init(I2PAppContext ctx, boolean inbound) {
|
||||
_context = ctx;
|
||||
_dataBuf = _dataCache.acquire();
|
||||
_data = _dataBuf.getData();
|
||||
_packet = new DatagramPacket(_data, MAX_PACKET_SIZE);
|
||||
//_packet = new DatagramPacket(_data, MAX_PACKET_SIZE);
|
||||
_packet.setData(_data);
|
||||
_isInbound = inbound;
|
||||
_initializeTime = _context.clock().now();
|
||||
_markedType = -1;
|
||||
_remoteHost = null;
|
||||
_released = false;
|
||||
}
|
||||
|
||||
/*
|
||||
public void initialize(int priority, long expiration, InetAddress host, int port) {
|
||||
_priority = (short)priority;
|
||||
_expiration = expiration;
|
||||
@ -99,6 +114,7 @@ public class UDPPacket {
|
||||
_released = false;
|
||||
_releasedBy = null;
|
||||
}
|
||||
*/
|
||||
|
||||
public void writeData(byte src[], int offset, int len) {
|
||||
verifyNotReleased();
|
||||
@ -129,8 +145,12 @@ public class UDPPacket {
|
||||
void setFragmentCount(int count) { _fragmentCount = count; }
|
||||
|
||||
public RemoteHostId getRemoteHost() {
|
||||
if (_remoteHost == null)
|
||||
_remoteHost = new RemoteHostId(_packet.getAddress().getAddress(), _packet.getPort());
|
||||
if (_remoteHost == null) {
|
||||
InetAddress addr = _packet.getAddress();
|
||||
byte ip[] = addr.getAddress();
|
||||
int port = _packet.getPort();
|
||||
_remoteHost = new RemoteHostId(ip, port);
|
||||
}
|
||||
return _remoteHost;
|
||||
}
|
||||
|
||||
@ -234,7 +254,7 @@ public class UDPPacket {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static UDPPacket acquire(I2PAppContext ctx) {
|
||||
public static UDPPacket acquire(I2PAppContext ctx, boolean inbound) {
|
||||
UDPPacket rv = null;
|
||||
if (CACHE) {
|
||||
synchronized (_packetCache) {
|
||||
@ -242,27 +262,12 @@ public class UDPPacket {
|
||||
rv = (UDPPacket)_packetCache.remove(0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (rv != null) {
|
||||
rv._context = ctx;
|
||||
//rv._log = ctx.logManager().getLog(UDPPacket.class);
|
||||
rv.resetBegin();
|
||||
Arrays.fill(rv._data, (byte)0x00);
|
||||
rv._markedType = -1;
|
||||
rv._dataBuf.setValid(0);
|
||||
rv._released = false;
|
||||
rv._releasedBy = null;
|
||||
rv._acquiredBy = null;
|
||||
rv.setPacketDataLength(0);
|
||||
synchronized (rv._packet) {
|
||||
//rv._packet.setLength(0);
|
||||
//rv._packet.setPort(1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (rv != null)
|
||||
rv.init(ctx, inbound);
|
||||
}
|
||||
if (rv == null)
|
||||
rv = new UDPPacket(ctx);
|
||||
rv = new UDPPacket(ctx, inbound);
|
||||
//if (rv._acquiredBy != null) {
|
||||
// _log.log(Log.CRIT, "Already acquired! current stack trace is:", new Exception());
|
||||
// _log.log(Log.CRIT, "Earlier acquired:", rv._acquiredBy);
|
||||
@ -277,15 +282,12 @@ public class UDPPacket {
|
||||
//_releasedBy = new Exception("released by");
|
||||
//_acquiredBy = null;
|
||||
//
|
||||
if (!CACHE) {
|
||||
_dataCache.release(_dataBuf);
|
||||
_dataCache.release(_dataBuf);
|
||||
if (!CACHE)
|
||||
return;
|
||||
}
|
||||
synchronized (_packetCache) {
|
||||
if (_packetCache.size() <= 64) {
|
||||
if (_packetCache.size() <= CACHE_SIZE) {
|
||||
_packetCache.add(this);
|
||||
} else {
|
||||
_dataCache.release(_dataBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,17 +270,17 @@ public class UDPPacketReader {
|
||||
public boolean readExtendedDataIncluded() {
|
||||
return flagSet(UDPPacket.DATA_FLAG_EXTENDED);
|
||||
}
|
||||
public long[] readACKs() {
|
||||
if (!readACKsIncluded()) return null;
|
||||
public int readACKCount() {
|
||||
if (!readACKsIncluded()) return 0;
|
||||
int off = readBodyOffset() + 1;
|
||||
int num = (int)DataHelper.fromLong(_message, off, 1);
|
||||
return (int)DataHelper.fromLong(_message, off, 1);
|
||||
}
|
||||
public long readACK(int index) {
|
||||
if (!readACKsIncluded()) return -1;
|
||||
int off = readBodyOffset() + 1;
|
||||
//int num = (int)DataHelper.fromLong(_message, off, 1);
|
||||
off++;
|
||||
long rv[] = new long[num];
|
||||
for (int i = 0; i < num; i++) {
|
||||
rv[i] = DataHelper.fromLong(_message, off, 4);
|
||||
off += 4;
|
||||
}
|
||||
return rv;
|
||||
return DataHelper.fromLong(_message, off + (4 * index), 4);
|
||||
}
|
||||
public ACKBitfield[] readACKBitfields() {
|
||||
if (!readACKBitfieldsIncluded()) return null;
|
||||
|
@ -42,6 +42,7 @@ public class UDPReceiver {
|
||||
_transport = transport;
|
||||
_runner = new Runner();
|
||||
_context.statManager().createRateStat("udp.receivePacketSize", "How large packets received are", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("udp.receiveRemaining", "How many packets are left sitting on the receiver's queue", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("udp.droppedInbound", "How many packet are queued up but not yet received when we drop", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("udp.droppedInboundProbabalistically", "How many packet we drop probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("udp.acceptedInboundProbabalistically", "How many packet we accept probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
|
||||
@ -145,6 +146,7 @@ public class UDPReceiver {
|
||||
}
|
||||
|
||||
// rejected
|
||||
packet.release();
|
||||
_context.statManager().addRateData("udp.droppedInbound", queueSize, headPeriod);
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
StringBuffer msg = new StringBuffer();
|
||||
@ -171,31 +173,36 @@ public class UDPReceiver {
|
||||
*
|
||||
*/
|
||||
public UDPPacket receiveNext() {
|
||||
UDPPacket rv = null;
|
||||
int remaining = 0;
|
||||
while (_keepRunning) {
|
||||
synchronized (_inboundQueue) {
|
||||
if (_inboundQueue.size() <= 0)
|
||||
try { _inboundQueue.wait(); } catch (InterruptedException ie) {}
|
||||
if (_inboundQueue.size() > 0) {
|
||||
UDPPacket rv = (UDPPacket)_inboundQueue.remove(0);
|
||||
if (_inboundQueue.size() > 0)
|
||||
rv = (UDPPacket)_inboundQueue.remove(0);
|
||||
remaining = _inboundQueue.size();
|
||||
if (remaining > 0)
|
||||
_inboundQueue.notifyAll();
|
||||
return rv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
_context.statManager().addRateData("udp.receiveRemaining", remaining, 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private class Runner implements Runnable {
|
||||
private boolean _socketChanged;
|
||||
public void run() {
|
||||
_socketChanged = false;
|
||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().createRequest();
|
||||
while (_keepRunning) {
|
||||
if (_socketChanged) {
|
||||
Thread.currentThread().setName(_name + "." + _id);
|
||||
_socketChanged = false;
|
||||
}
|
||||
UDPPacket packet = UDPPacket.acquire(_context);
|
||||
UDPPacket packet = UDPPacket.acquire(_context, true);
|
||||
|
||||
// block before we read...
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -217,7 +224,9 @@ public class UDPReceiver {
|
||||
// and block after we know how much we read but before
|
||||
// we release the packet to the inbound queue
|
||||
if (size > 0) {
|
||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(size, "UDP receiver");
|
||||
//FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(size, "UDP receiver");
|
||||
//_context.bandwidthLimiter().requestInbound(req, size, "UDP receiver");
|
||||
req = _context.bandwidthLimiter().requestInbound(size, "UDP receiver");
|
||||
while (req.getPendingInboundRequested() > 0)
|
||||
req.waitForNextAllocation();
|
||||
|
||||
|
@ -178,6 +178,7 @@ public class UDPSender {
|
||||
|
||||
private class Runner implements Runnable {
|
||||
private boolean _socketChanged;
|
||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().createRequest();
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Running the UDP sender");
|
||||
@ -196,7 +197,8 @@ public class UDPSender {
|
||||
int size = packet.getPacket().getLength();
|
||||
int size2 = packet.getPacket().getLength();
|
||||
if (size > 0) {
|
||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestOutbound(size, "UDP sender");
|
||||
//_context.bandwidthLimiter().requestOutbound(req, size, "UDP sender");
|
||||
req = _context.bandwidthLimiter().requestOutbound(size, "UDP sender");
|
||||
while (req.getPendingOutboundRequested() > 0)
|
||||
req.waitForNextAllocation();
|
||||
}
|
||||
@ -209,7 +211,7 @@ public class UDPSender {
|
||||
//_log.debug("Sending packet: (size="+size + "/"+size2 +")\nraw: " + Base64.encode(packet.getPacket().getData(), 0, size));
|
||||
}
|
||||
|
||||
_context.statManager().addRateData("udp.sendPacketSize." + packet.getMessageType(), size, packet.getFragmentCount());
|
||||
//_context.statManager().addRateData("udp.sendPacketSize." + packet.getMessageType(), size, packet.getFragmentCount());
|
||||
|
||||
//packet.getPacket().setLength(size);
|
||||
try {
|
||||
|
@ -961,13 +961,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if ( (msg.getPeer() != null) &&
|
||||
( (msg.getMaxSends() >= OutboundMessageFragments.MAX_VOLLEYS) ||
|
||||
(msg.isExpired())) ) {
|
||||
long recvDelay = _context.clock().now() - msg.getPeer().getLastReceiveTime();
|
||||
long sendDelay = _context.clock().now() - msg.getPeer().getLastSendFullyTime();
|
||||
if (m != null)
|
||||
m.timestamp("message failure - volleys = " + msg.getMaxSends()
|
||||
+ " lastReceived: " + recvDelay
|
||||
+ " lastSentFully: " + sendDelay
|
||||
+ " expired? " + msg.isExpired());
|
||||
//long recvDelay = _context.clock().now() - msg.getPeer().getLastReceiveTime();
|
||||
//long sendDelay = _context.clock().now() - msg.getPeer().getLastSendFullyTime();
|
||||
//if (m != null)
|
||||
// m.timestamp("message failure - volleys = " + msg.getMaxSends()
|
||||
// + " lastReceived: " + recvDelay
|
||||
// + " lastSentFully: " + sendDelay
|
||||
// + " expired? " + msg.isExpired());
|
||||
consecutive = msg.getPeer().incrementConsecutiveFailedSends();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Consecutive failure #" + consecutive
|
||||
|
Reference in New Issue
Block a user