forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head 871765966dc474b763ff0d5c017bed7535981c1e)
to branch 'i2p.i2p.zzz.test' (head 096242c22aa550274cb383a6a0c984cef07ae08c)
This commit is contained in:
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 19;
|
||||
public final static long BUILD = 22;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -86,7 +86,7 @@ class ClientConnectionRunner {
|
||||
private ClientWriterRunner _writer;
|
||||
private Hash _destHashCache;
|
||||
/** are we, uh, dead */
|
||||
private boolean _dead;
|
||||
private volatile boolean _dead;
|
||||
/** For outbound traffic. true if i2cp.messageReliability = "none"; @since 0.8.1 */
|
||||
private boolean _dontSendMSM;
|
||||
private final AtomicInteger _messageId; // messageId counter
|
||||
@@ -463,10 +463,10 @@ class ClientConnectionRunner {
|
||||
}
|
||||
|
||||
private class Rerequest implements SimpleTimer.TimedEvent {
|
||||
private LeaseSet _ls;
|
||||
private long _expirationTime;
|
||||
private Job _onCreate;
|
||||
private Job _onFailed;
|
||||
private final LeaseSet _ls;
|
||||
private final long _expirationTime;
|
||||
private final Job _onCreate;
|
||||
private final Job _onFailed;
|
||||
public Rerequest(LeaseSet ls, long expirationTime, Job onCreate, Job onFailed) {
|
||||
_ls = ls;
|
||||
_expirationTime = expirationTime;
|
||||
|
||||
@@ -31,8 +31,8 @@ class ClientListenerRunner implements Runnable {
|
||||
protected ServerSocket _socket;
|
||||
protected final int _port;
|
||||
protected final boolean _bindAllInterfaces;
|
||||
protected boolean _running;
|
||||
protected boolean _listening;
|
||||
protected volatile boolean _running;
|
||||
protected volatile boolean _listening;
|
||||
|
||||
public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces";
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class ClientManager {
|
||||
private final HashMap<Destination, ClientConnectionRunner> _runners; // Destination --> ClientConnectionRunner
|
||||
private final Set<ClientConnectionRunner> _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
|
||||
private final RouterContext _ctx;
|
||||
private boolean _isStarted;
|
||||
private volatile boolean _isStarted;
|
||||
|
||||
/** Disable external interface, allow internal clients only @since 0.8.3 */
|
||||
private static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface";
|
||||
|
||||
@@ -38,7 +38,7 @@ import net.i2p.util.Log;
|
||||
public class ClientManagerFacadeImpl extends ClientManagerFacade implements InternalClientManager {
|
||||
private final Log _log;
|
||||
private ClientManager _manager;
|
||||
private RouterContext _context;
|
||||
private final RouterContext _context;
|
||||
/** note that this is different than the property the client side uses, i2cp.tcp.port */
|
||||
public final static String PROP_CLIENT_PORT = "i2cp.port";
|
||||
public final static int DEFAULT_PORT = 7654;
|
||||
|
||||
@@ -17,10 +17,10 @@ import net.i2p.util.Log;
|
||||
* @author zzz modded to use concurrent
|
||||
*/
|
||||
class ClientWriterRunner implements Runnable {
|
||||
private BlockingQueue<I2CPMessage> _messagesToWrite;
|
||||
private ClientConnectionRunner _runner;
|
||||
private Log _log;
|
||||
private long _id;
|
||||
private final BlockingQueue<I2CPMessage> _messagesToWrite;
|
||||
private final ClientConnectionRunner _runner;
|
||||
private final Log _log;
|
||||
private final long _id;
|
||||
private static long __id = 0;
|
||||
|
||||
public ClientWriterRunner(RouterContext context, ClientConnectionRunner runner) {
|
||||
|
||||
@@ -24,8 +24,8 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class CreateSessionJob extends JobImpl {
|
||||
private Log _log;
|
||||
private ClientConnectionRunner _runner;
|
||||
private final Log _log;
|
||||
private final ClientConnectionRunner _runner;
|
||||
|
||||
public CreateSessionJob(RouterContext context, ClientConnectionRunner runner) {
|
||||
super(context);
|
||||
|
||||
@@ -20,12 +20,12 @@ import net.i2p.router.Job;
|
||||
*/
|
||||
class LeaseRequestState {
|
||||
private LeaseSet _grantedLeaseSet;
|
||||
private LeaseSet _requestedLeaseSet;
|
||||
private PrivateKey _leaseSetPrivateKey;
|
||||
private SigningPrivateKey _leaseSetSigningPrivateKey;
|
||||
private Job _onGranted;
|
||||
private Job _onFailed;
|
||||
private long _expiration;
|
||||
private final LeaseSet _requestedLeaseSet;
|
||||
//private PrivateKey _leaseSetPrivateKey;
|
||||
//private SigningPrivateKey _leaseSetSigningPrivateKey;
|
||||
private final Job _onGranted;
|
||||
private final Job _onFailed;
|
||||
private final long _expiration;
|
||||
private boolean _successful;
|
||||
|
||||
public LeaseRequestState(Job onGranted, Job onFailed, long expiration, LeaseSet requested) {
|
||||
@@ -35,26 +35,34 @@ class LeaseRequestState {
|
||||
_requestedLeaseSet = requested;
|
||||
}
|
||||
|
||||
/** created lease set from client */
|
||||
/** created lease set from client - FIXME always null */
|
||||
public LeaseSet getGranted() { return _grantedLeaseSet; }
|
||||
/** FIXME unused - why? */
|
||||
public void setGranted(LeaseSet ls) { _grantedLeaseSet = ls; }
|
||||
|
||||
/** lease set that is being requested */
|
||||
public LeaseSet getRequested() { return _requestedLeaseSet; }
|
||||
public void setRequested(LeaseSet ls) { _requestedLeaseSet = ls; }
|
||||
//public void setRequested(LeaseSet ls) { _requestedLeaseSet = ls; }
|
||||
|
||||
/** the private encryption key received regarding the lease set */
|
||||
public PrivateKey getPrivateKey() { return _leaseSetPrivateKey; }
|
||||
public void getPrivateKey(PrivateKey pk) { _leaseSetPrivateKey = pk; }
|
||||
//public PrivateKey getPrivateKey() { return _leaseSetPrivateKey; }
|
||||
//public void setPrivateKey(PrivateKey pk) { _leaseSetPrivateKey = pk; }
|
||||
|
||||
/** the private signing key received regarding the lease set (for revocation) */
|
||||
public SigningPrivateKey getSigningPrivateKey() { return _leaseSetSigningPrivateKey; }
|
||||
public void getSigningPrivateKey(SigningPrivateKey spk) { _leaseSetSigningPrivateKey = spk; }
|
||||
//public SigningPrivateKey getSigningPrivateKey() { return _leaseSetSigningPrivateKey; }
|
||||
//public void setSigningPrivateKey(SigningPrivateKey spk) { _leaseSetSigningPrivateKey = spk; }
|
||||
|
||||
/** what to do once the lease set is created */
|
||||
public Job getOnGranted() { return _onGranted; }
|
||||
public void setOnGranted(Job jb) { _onGranted = jb; }
|
||||
//public void setOnGranted(Job jb) { _onGranted = jb; }
|
||||
|
||||
/** what to do if the lease set create fails / times out */
|
||||
public Job getOnFailed() { return _onFailed; }
|
||||
public void setOnFailed(Job jb) { _onFailed = jb; }
|
||||
//public void setOnFailed(Job jb) { _onFailed = jb; }
|
||||
|
||||
/** when the request for the lease set expires */
|
||||
public long getExpiration() { return _expiration; }
|
||||
|
||||
/** whether the request was successful in the time allotted */
|
||||
public boolean getIsSuccessful() { return _successful; }
|
||||
public void setIsSuccessful(boolean is) { _successful = is; }
|
||||
|
||||
@@ -16,8 +16,8 @@ import net.i2p.router.RouterContext;
|
||||
* Look up the lease of a hash, to convert it to a Destination for the client
|
||||
*/
|
||||
class LookupDestJob extends JobImpl {
|
||||
private ClientConnectionRunner _runner;
|
||||
private Hash _hash;
|
||||
private final ClientConnectionRunner _runner;
|
||||
private final Hash _hash;
|
||||
|
||||
public LookupDestJob(RouterContext context, ClientConnectionRunner runner, Hash h) {
|
||||
super(context);
|
||||
|
||||
@@ -22,9 +22,9 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class MessageReceivedJob extends JobImpl {
|
||||
private Log _log;
|
||||
private ClientConnectionRunner _runner;
|
||||
private Payload _payload;
|
||||
private final Log _log;
|
||||
private final ClientConnectionRunner _runner;
|
||||
private final Payload _payload;
|
||||
public MessageReceivedJob(RouterContext ctx, ClientConnectionRunner runner, Destination toDest, Destination fromDest, Payload payload) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(MessageReceivedJob.class);
|
||||
|
||||
@@ -21,10 +21,10 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class ReportAbuseJob extends JobImpl {
|
||||
private Log _log;
|
||||
private ClientConnectionRunner _runner;
|
||||
private String _reason;
|
||||
private int _severity;
|
||||
private final Log _log;
|
||||
private final ClientConnectionRunner _runner;
|
||||
private final String _reason;
|
||||
private final int _severity;
|
||||
public ReportAbuseJob(RouterContext context, ClientConnectionRunner runner, String reason, int severity) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(ReportAbuseJob.class);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Reseeder {
|
||||
public static final String DEFAULT_SEED_URL =
|
||||
"http://netdb.i2p2.de/" + "," +
|
||||
"http://reseed.i2p-projekt.de/" + "," +
|
||||
"http://forum.i2p2.de/netdb/" + "," +
|
||||
// "http://forum.i2p2.de/netdb/" + "," +
|
||||
"http://euve5653.vserver.de/netDb/" + "," +
|
||||
// "http://r31453.ovh.net/static_media/files/netDb/" + "," +
|
||||
"http://cowpuncher.drollette.com/netdb/" + "," +
|
||||
@@ -73,7 +73,7 @@ public class Reseeder {
|
||||
/** @since 0.8.2 */
|
||||
public static final String DEFAULT_SSL_SEED_URL =
|
||||
"https://netdb.i2p2.de/" + "," +
|
||||
"https://forum.i2p2.de/netdb/" + "," +
|
||||
// "https://forum.i2p2.de/netdb/" + "," +
|
||||
"https://euve5653.vserver.de/netDb/" + "," +
|
||||
"https://reseed.i2p-projekt.de/" + "," +
|
||||
// "https://r31453.ovh.net/static_media/files/netDb/" + "," +
|
||||
|
||||
@@ -33,7 +33,8 @@ public class StartupJob extends JobImpl {
|
||||
|
||||
public String getName() { return "Startup Router"; }
|
||||
public void runJob() {
|
||||
getContext().jobQueue().addJob(new LoadClientAppsJob(getContext()));
|
||||
if (!System.getProperty("java.vendor").contains("Android"))
|
||||
getContext().jobQueue().addJob(new LoadClientAppsJob(getContext()));
|
||||
getContext().statPublisher().startup();
|
||||
getContext().jobQueue().addJob(new LoadRouterInfoJob(getContext()));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class Spinner extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sleep(60*1000);
|
||||
sleep(5*60*1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,9 @@ class Reader {
|
||||
already = true;
|
||||
} else {
|
||||
_pendingConnections.add(con);
|
||||
// only notify here if added?
|
||||
}
|
||||
_pendingConnections.notifyAll();
|
||||
_pendingConnections.notify();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("wantsRead: " + con + " already live? " + already);
|
||||
@@ -75,7 +76,8 @@ class Reader {
|
||||
synchronized (_pendingConnections) {
|
||||
_readAfterLive.remove(con);
|
||||
_pendingConnections.remove(con);
|
||||
_pendingConnections.notifyAll();
|
||||
// necessary?
|
||||
_pendingConnections.notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,9 @@ class Writer {
|
||||
already = true;
|
||||
} else {
|
||||
pending = _pendingConnections.add(con);
|
||||
// only notify here if added?
|
||||
}
|
||||
_pendingConnections.notifyAll();
|
||||
_pendingConnections.notify();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("wantsWrite: " + con + " already live? " + already + " added to pending? " + pending + ": " + source);
|
||||
@@ -73,7 +74,8 @@ class Writer {
|
||||
synchronized (_pendingConnections) {
|
||||
_writeAfterLive.remove(con);
|
||||
_pendingConnections.remove(con);
|
||||
_pendingConnections.notifyAll();
|
||||
// necessary?
|
||||
_pendingConnections.notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ class OutboundMessageState {
|
||||
private static final int MAX_ENTRIES = 64;
|
||||
/** would two caches, one for small and one for large messages, be better? */
|
||||
private static final ByteCache _cache = ByteCache.getInstance(MAX_ENTRIES, MAX_MSG_SIZE);
|
||||
|
||||
private static final long EXPIRATION = 10*1000;
|
||||
|
||||
public OutboundMessageState(I2PAppContext context) {
|
||||
_context = context;
|
||||
@@ -64,6 +66,7 @@ class OutboundMessageState {
|
||||
|
||||
/**
|
||||
* Called from UDPTransport
|
||||
* TODO make two constructors, remove this, and make more things final
|
||||
* @return success
|
||||
*/
|
||||
public boolean initialize(I2NPMessage msg, PeerState peer) {
|
||||
@@ -82,6 +85,7 @@ class OutboundMessageState {
|
||||
|
||||
/**
|
||||
* Called from OutboundMessageFragments
|
||||
* TODO make two constructors, remove this, and make more things final
|
||||
* @return success
|
||||
*/
|
||||
public boolean initialize(OutNetMessage m, I2NPMessage msg) {
|
||||
@@ -121,7 +125,7 @@ class OutboundMessageState {
|
||||
|
||||
_startedOn = _context.clock().now();
|
||||
_nextSendTime = _startedOn;
|
||||
_expiration = _startedOn + 10*1000;
|
||||
_expiration = _startedOn + EXPIRATION;
|
||||
//_expiration = msg.getExpiration();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
|
||||
@@ -38,7 +38,7 @@ class PacketPusher implements Runnable {
|
||||
if (packets != null) {
|
||||
for (int i = 0; i < packets.length; i++) {
|
||||
if (packets[i] != null) // null for ACKed fragments
|
||||
//_sender.add(packets[i], 0); // 0 does not block //100); // blocks for up to 100ms
|
||||
// BLOCKING if queue is full
|
||||
_sender.add(packets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +217,13 @@ class PeerState {
|
||||
private static final int MINIMUM_WINDOW_BYTES = DEFAULT_SEND_WINDOW_BYTES;
|
||||
private static final int MAX_SEND_WINDOW_BYTES = 1024*1024;
|
||||
|
||||
/**
|
||||
* Was 32 before 0.9.2, but since the streaming lib goes up to 128,
|
||||
* we would just drop our own msgs right away during slow start.
|
||||
* May need to adjust based on memory.
|
||||
*/
|
||||
private static final int MAX_SEND_MSGS_PENDING = 128;
|
||||
|
||||
/*
|
||||
* 596 gives us 588 IP byes, 568 UDP bytes, and with an SSU data message,
|
||||
* 522 fragment bytes, which is enough to send a tunnel data message in 2
|
||||
@@ -1181,6 +1188,14 @@ class PeerState {
|
||||
|
||||
RemoteHostId getRemoteHostId() { return _remoteHostId; }
|
||||
|
||||
/**
|
||||
* TODO should this use a queue, separate from the list of msgs pending an ack?
|
||||
* TODO bring back tail drop?
|
||||
* TODO priority queue? (we don't implement priorities in SSU now)
|
||||
* TODO backlog / pushback / block instead of dropping? Can't really block here.
|
||||
* TODO SSU does not support isBacklogged() now
|
||||
* @return total pending messages
|
||||
*/
|
||||
public int add(OutboundMessageState state) {
|
||||
if (_dead) {
|
||||
_transport.failed(state, false);
|
||||
@@ -1193,8 +1208,8 @@ class PeerState {
|
||||
boolean fail = false;
|
||||
synchronized (_outboundMessages) {
|
||||
rv = _outboundMessages.size() + 1;
|
||||
if (rv > 32) {
|
||||
// 32 queued messages? to *one* peer? nuh uh.
|
||||
if (rv > MAX_SEND_MSGS_PENDING) {
|
||||
// too many queued messages to one peer? nuh uh.
|
||||
fail = true;
|
||||
rv--;
|
||||
|
||||
@@ -1240,8 +1255,11 @@ class PeerState {
|
||||
_outboundMessages.add(state);
|
||||
}
|
||||
}
|
||||
if (fail)
|
||||
if (fail) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dropping msg, OB queue full for " + toString());
|
||||
_transport.failed(state, false);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1278,6 +1296,10 @@ class PeerState {
|
||||
|
||||
/**
|
||||
* Expire / complete any outbound messages
|
||||
* High usage -
|
||||
* OutboundMessageFragments.getNextVolley() calls this 1st.
|
||||
* TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times.
|
||||
*
|
||||
* @return number of active outbound messages remaining
|
||||
*/
|
||||
public int finishMessages() {
|
||||
@@ -1350,14 +1372,20 @@ class PeerState {
|
||||
|
||||
/**
|
||||
* Pick a message we want to send and allocate it out of our window
|
||||
* @return allocated message to send, or null if no messages or no resources
|
||||
* High usage -
|
||||
* OutboundMessageFragments.getNextVolley() calls this 2nd, if finishMessages() returned > 0.
|
||||
* TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times.
|
||||
*
|
||||
* @return allocated message to send, or null if no messages or no resources
|
||||
*/
|
||||
public OutboundMessageState allocateSend() {
|
||||
if (_dead) return null;
|
||||
synchronized (_outboundMessages) {
|
||||
for (OutboundMessageState state : _outboundMessages) {
|
||||
if (locked_shouldSend(state)) {
|
||||
// We have 3 return values, because if allocateSendingBytes() returns false,
|
||||
// then we can stop iterating.
|
||||
ShouldSend should = locked_shouldSend(state);
|
||||
if (should == ShouldSend.YES) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Allocate sending to " + _remotePeer + ": " + state.getMessageId());
|
||||
/*
|
||||
@@ -1369,6 +1397,12 @@ class PeerState {
|
||||
}
|
||||
*/
|
||||
return state;
|
||||
} else if (should == ShouldSend.NO_BW) {
|
||||
// no more bandwidth available
|
||||
// we don't bother looking for a smaller msg that would fit.
|
||||
// By not looking further, we keep strict sending order, and that allows
|
||||
// some efficiency in acked() below.
|
||||
break;
|
||||
} /* else {
|
||||
OutNetMessage msg = state.getMessage();
|
||||
if (msg != null)
|
||||
@@ -1382,6 +1416,10 @@ class PeerState {
|
||||
}
|
||||
|
||||
/**
|
||||
* High usage -
|
||||
* OutboundMessageFragments.getNextVolley() calls this 3rd, if allocateSend() returned null.
|
||||
* TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times.
|
||||
*
|
||||
* @return how long to wait before sending, or Integer.MAX_VALUE if we have nothing to send.
|
||||
* If ready now, will return 0 or a negative value.
|
||||
*/
|
||||
@@ -1396,6 +1434,9 @@ class PeerState {
|
||||
}
|
||||
for (OutboundMessageState state : _outboundMessages) {
|
||||
int delay = (int)(state.getNextSendTime() - now);
|
||||
// short circuit once we hit something ready to go
|
||||
if (delay <= 0)
|
||||
return delay;
|
||||
if (delay < rv)
|
||||
rv = delay;
|
||||
}
|
||||
@@ -1435,7 +1476,13 @@ class PeerState {
|
||||
return mtu - (PacketBuilder.MIN_DATA_PACKET_OVERHEAD + MIN_ACK_SIZE);
|
||||
}
|
||||
|
||||
private boolean locked_shouldSend(OutboundMessageState state) {
|
||||
private enum ShouldSend { YES, NO, NO_BW };
|
||||
|
||||
/**
|
||||
* Have 3 return values, because if allocateSendingBytes() returns false,
|
||||
* then allocateSend() can stop iterating
|
||||
*/
|
||||
private ShouldSend locked_shouldSend(OutboundMessageState state) {
|
||||
long now = _context.clock().now();
|
||||
if (state.getNextSendTime() <= now) {
|
||||
if (!state.isFragmented()) {
|
||||
@@ -1465,7 +1512,7 @@ class PeerState {
|
||||
} else if ( (max <= 0) || (THROTTLE_RESENDS) ) {
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("choked, with another message retransmitting");
|
||||
return false;
|
||||
return ShouldSend.NO;
|
||||
} else {
|
||||
//if (state.getMessage() != null)
|
||||
// state.getMessage().timestamp("another message is retransmitting, but since we've already begun sending...");
|
||||
@@ -1491,7 +1538,7 @@ class PeerState {
|
||||
|
||||
//if (peer.getSendWindowBytesRemaining() > 0)
|
||||
// _throttle.unchoke(peer.getRemotePeer());
|
||||
return true;
|
||||
return ShouldSend.YES;
|
||||
} else {
|
||||
_context.statManager().addRateData("udp.sendRejected", state.getPushCount(), state.getLifetime());
|
||||
//if (state.getMessage() != null)
|
||||
@@ -1510,15 +1557,16 @@ class PeerState {
|
||||
// state.getMessage().timestamp("choked, not enough available, wsize="
|
||||
// + getSendWindowBytes() + " available="
|
||||
// + getSendWindowBytesRemaining());
|
||||
return false;
|
||||
return ShouldSend.NO_BW;
|
||||
}
|
||||
} // nextTime <= now
|
||||
|
||||
return false;
|
||||
return ShouldSend.NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* A full ACK was received.
|
||||
* TODO if messages awaiting ack were a HashSet this would be faster.
|
||||
*
|
||||
* @return true if the message was acked for the first time
|
||||
*/
|
||||
@@ -1531,6 +1579,11 @@ class PeerState {
|
||||
if (state.getMessageId() == messageId) {
|
||||
iter.remove();
|
||||
break;
|
||||
} else if (state.getPushCount() <= 0) {
|
||||
// _outboundMessages is ordered, so once we get to a msg that
|
||||
// hasn't been transmitted yet, we can stop
|
||||
state = null;
|
||||
break;
|
||||
} else {
|
||||
state = null;
|
||||
}
|
||||
@@ -1600,6 +1653,11 @@ class PeerState {
|
||||
_retransmitter = null;
|
||||
}
|
||||
break;
|
||||
} else if (state.getPushCount() <= 0) {
|
||||
// _outboundMessages is ordered, so once we get to a msg that
|
||||
// hasn't been transmitted yet, we can stop
|
||||
state = null;
|
||||
break;
|
||||
} else {
|
||||
state = null;
|
||||
}
|
||||
|
||||
@@ -136,13 +136,10 @@ class UDPEndpoint {
|
||||
/**
|
||||
* Add the packet to the outobund queue to be sent ASAP (as allowed by
|
||||
* the bandwidth limiter)
|
||||
*
|
||||
* @return ZERO (used to be number of packets in the queue)
|
||||
* BLOCKING if queue is full.
|
||||
*/
|
||||
public int send(UDPPacket packet) {
|
||||
if (_sender == null)
|
||||
return 0;
|
||||
return _sender.add(packet);
|
||||
public void send(UDPPacket packet) {
|
||||
_sender.add(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,4 +151,12 @@ class UDPEndpoint {
|
||||
return null;
|
||||
return _receiver.receiveNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear outbound queue, probably in preparation for sending destroy() to everybody.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void clearOutbound() {
|
||||
_sender.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,17 @@ class UDPSender {
|
||||
private final Runner _runner;
|
||||
private static final int TYPE_POISON = 99999;
|
||||
|
||||
//private static final int MAX_QUEUED = 4;
|
||||
private static final int MIN_QUEUE_SIZE = 64;
|
||||
private static final int MAX_QUEUE_SIZE = 384;
|
||||
|
||||
public UDPSender(RouterContext ctx, DatagramSocket socket, String name) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(UDPSender.class);
|
||||
_outboundQueue = new LinkedBlockingQueue();
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
if (maxMemory == Long.MAX_VALUE)
|
||||
maxMemory = 96*1024*1024l;
|
||||
int qsize = (int) Math.max(MIN_QUEUE_SIZE, Math.min(MAX_QUEUE_SIZE, maxMemory / (1024*1024)));
|
||||
_outboundQueue = new LinkedBlockingQueue(qsize);
|
||||
_socket = socket;
|
||||
_runner = new Runner();
|
||||
_name = name;
|
||||
@@ -81,6 +86,14 @@ class UDPSender {
|
||||
_outboundQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear outbound queue, probably in preparation for sending destroy() to everybody.
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void clear() {
|
||||
_outboundQueue.clear();
|
||||
}
|
||||
|
||||
/*********
|
||||
public DatagramSocket updateListeningPort(DatagramSocket socket, int newPort) {
|
||||
return _runner.updateListeningPort(socket, newPort);
|
||||
@@ -93,10 +106,9 @@ class UDPSender {
|
||||
* available, if requested, otherwise it returns immediately
|
||||
*
|
||||
* @param blockTime how long to block IGNORED
|
||||
* @return ZERO (used to be number of packets in the queue)
|
||||
* @deprecated use add(packet)
|
||||
*/
|
||||
public int add(UDPPacket packet, int blockTime) {
|
||||
public void add(UDPPacket packet, int blockTime) {
|
||||
/********
|
||||
//long expiration = _context.clock().now() + blockTime;
|
||||
int remaining = -1;
|
||||
@@ -148,31 +160,32 @@ class UDPSender {
|
||||
_log.debug("Added the packet onto the queue with " + remaining + " remaining and a lifetime of " + lifetime);
|
||||
return remaining;
|
||||
********/
|
||||
return add(packet);
|
||||
add(packet);
|
||||
}
|
||||
|
||||
private static final int MAX_HEAD_LIFETIME = 1000;
|
||||
private static final int MAX_HEAD_LIFETIME = 3*1000;
|
||||
|
||||
/**
|
||||
* Put it on the queue
|
||||
* @return ZERO (used to be number of packets in the queue)
|
||||
* Put it on the queue.
|
||||
* BLOCKING if queue is full (backs up PacketPusher thread)
|
||||
*/
|
||||
public int add(UDPPacket packet) {
|
||||
if (packet == null || !_keepRunning) return 0;
|
||||
int size = 0;
|
||||
public void add(UDPPacket packet) {
|
||||
if (packet == null || !_keepRunning) return;
|
||||
int psz = packet.getPacket().getLength();
|
||||
if (psz > PeerState.LARGE_MTU) {
|
||||
_log.error("Dropping large UDP packet " + psz + " bytes: " + packet);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_outboundQueue.put(packet);
|
||||
} catch (InterruptedException ie) {
|
||||
return;
|
||||
}
|
||||
_outboundQueue.offer(packet);
|
||||
//size = _outboundQueue.size();
|
||||
//_context.statManager().addRateData("udp.sendQueueSize", size, lifetime);
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
size = _outboundQueue.size();
|
||||
_log.debug("Added the packet onto the queue with " + size + " remaining and a lifetime of " + packet.getLifetime());
|
||||
_log.debug("Added the packet onto the queue with a lifetime of " + packet.getLifetime());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private class Runner implements Runnable {
|
||||
|
||||
@@ -1119,17 +1119,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
/**
|
||||
* This sends it directly out, bypassing OutboundMessageFragments
|
||||
* and the PacketPusher. The only queueing is for the bandwidth limiter.
|
||||
*
|
||||
* @return ZERO (used to be number of packets in the queue)
|
||||
* BLOCKING if OB queue is full.
|
||||
*/
|
||||
int send(UDPPacket packet) {
|
||||
void send(UDPPacket packet) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Sending packet " + packet);
|
||||
return _endpoint.send(packet);
|
||||
_endpoint.send(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a session destroy message, bypassing OMF and PacketPusher.
|
||||
* BLOCKING if OB queue is full.
|
||||
*
|
||||
* @since 0.8.9
|
||||
*/
|
||||
@@ -1145,10 +1145,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
/**
|
||||
* Send a session destroy message to everybody
|
||||
* BLOCKING if OB queue is full.
|
||||
*
|
||||
* @since 0.8.9
|
||||
*/
|
||||
private void destroyAll() {
|
||||
_endpoint.clearOutbound();
|
||||
int howMany = _peersByIdent.size();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Sending destroy to : " + howMany + " peers");
|
||||
|
||||
@@ -6,7 +6,7 @@ package net.i2p.router.util;
|
||||
* No license, free to use
|
||||
*/
|
||||
|
||||
//import java.util.ArrayList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
@@ -89,6 +89,13 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
/** Used to narrow the range to take random indexes from */
|
||||
private int lower, upper;
|
||||
|
||||
private static final boolean isAndroid = System.getProperty("java.vendor").contains("Android");
|
||||
|
||||
static {
|
||||
if (isAndroid)
|
||||
testAndroid();
|
||||
}
|
||||
|
||||
public RandomIterator(List<E> list){
|
||||
this.list = list;
|
||||
LIST_SIZE = list.size();
|
||||
@@ -129,9 +136,10 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
// I2P - ensure lower and upper are always clear
|
||||
if (hasNext()) {
|
||||
if (index == lower)
|
||||
lower = served.nextClearBit(lower);
|
||||
// workaround for Android ICS bug - see below
|
||||
lower = isAndroid ? nextClearBit(index) : served.nextClearBit(index);
|
||||
else if (index == upper)
|
||||
upper = previousClearBit(upper - 1);
|
||||
upper = previousClearBit(index - 1);
|
||||
}
|
||||
return list.get(index);
|
||||
}
|
||||
@@ -146,6 +154,20 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for bug in Android (ICS only?)
|
||||
* http://code.google.com/p/android/issues/detail?id=31036
|
||||
* @since 0.9.2
|
||||
*/
|
||||
private int nextClearBit(int n) {
|
||||
for (int i = n; i <= upper; i++) {
|
||||
if (!served.get(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@@ -153,19 +175,16 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String[] args) {
|
||||
System.out.println("\n testing with 0");
|
||||
testAndroid();
|
||||
test(0);
|
||||
System.out.println("\n testing with 1");
|
||||
test(1);
|
||||
System.out.println("\n testing with 2");
|
||||
test(2);
|
||||
System.out.println("\n testing with 1000");
|
||||
test(1000);
|
||||
}
|
||||
|
||||
public static void test(int n) {
|
||||
private static void test(int n) {
|
||||
System.out.println("testing with " + n);
|
||||
List<Integer> l = new ArrayList(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
l.add(Integer.valueOf(i));
|
||||
@@ -174,5 +193,23 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
System.out.println(iter.next().toString());
|
||||
}
|
||||
}
|
||||
*****/
|
||||
|
||||
/**
|
||||
* Test case from android ticket above
|
||||
* @since 0.9.2
|
||||
*/
|
||||
private static void testAndroid() {
|
||||
System.out.println("checking for Android bug");
|
||||
BitSet theBitSet = new BitSet(864);
|
||||
for (int exp =0; exp < 864; exp++) {
|
||||
int act = theBitSet.nextClearBit(0);
|
||||
if (exp != act) {
|
||||
System.err.println(String.format("Test failed for: exp=%d, act=%d", exp, act));
|
||||
System.err.println("Android BitSet bug detected, workaround implemented!");
|
||||
return;
|
||||
}
|
||||
theBitSet.set(exp);
|
||||
}
|
||||
System.err.println("Android BitSet bug NOT detected, no workaround needed!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user