forked from I2P_Developers/i2p.i2p
182 lines
5.7 KiB
Java
182 lines
5.7 KiB
Java
/*
|
|
* Released into the public domain
|
|
* with no warranty of any kind, either expressed or implied.
|
|
*/
|
|
package org.klomp.snark;
|
|
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
|
|
import net.i2p.client.I2PSession;
|
|
import net.i2p.client.streaming.I2PSocketManager;
|
|
import net.i2p.util.Log;
|
|
import net.i2p.util.SimpleTimer2;
|
|
|
|
/**
|
|
* Periodically check for idle condition based on connected peers,
|
|
* and reduce/restore tunnel count as necessary.
|
|
* We can't use the I2CP idle detector because it's based on traffic,
|
|
* so DHT and announces would keep it non-idle.
|
|
*
|
|
* @since 0.9.7
|
|
*/
|
|
class IdleChecker extends SimpleTimer2.TimedEvent {
|
|
|
|
private final SnarkManager _mgr;
|
|
private final I2PSnarkUtil _util;
|
|
private final PeerCoordinatorSet _pcs;
|
|
private final Log _log;
|
|
private int _consec;
|
|
private int _consecNotRunning;
|
|
private boolean _isIdle;
|
|
private String _lastIn = "3";
|
|
private String _lastOut = "3";
|
|
private final Object _lock = new Object();
|
|
|
|
private static final long CHECK_TIME = 63*1000;
|
|
private static final int MAX_CONSEC_IDLE = 4;
|
|
private static final int MAX_CONSEC_NOT_RUNNING = 20;
|
|
|
|
/**
|
|
* Caller must schedule
|
|
*/
|
|
public IdleChecker(SnarkManager mgr, PeerCoordinatorSet pcs) {
|
|
super(mgr.util().getContext().simpleTimer2());
|
|
_util = mgr.util();
|
|
_log = _util.getContext().logManager().getLog(IdleChecker.class);
|
|
_mgr = mgr;
|
|
_pcs = pcs;
|
|
}
|
|
|
|
public void timeReached() {
|
|
synchronized (_lock) {
|
|
locked_timeReached();
|
|
}
|
|
}
|
|
|
|
private void locked_timeReached() {
|
|
if (_util.connected()) {
|
|
boolean torrentRunning = false;
|
|
int peerCount = 0;
|
|
for (PeerCoordinator pc : _pcs) {
|
|
if (!pc.halted()) {
|
|
torrentRunning = true;
|
|
peerCount += pc.getPeers();
|
|
}
|
|
}
|
|
|
|
if (torrentRunning) {
|
|
_consecNotRunning = 0;
|
|
} else {
|
|
if (_consecNotRunning++ >= MAX_CONSEC_NOT_RUNNING) {
|
|
if (_log.shouldLog(Log.WARN))
|
|
_log.warn("Closing tunnels on idle");
|
|
_util.disconnect();
|
|
_mgr.addMessage(_util.getString("No more torrents running.") + ' ' +
|
|
_util.getString("I2P tunnel closed."));
|
|
schedule(3 * CHECK_TIME);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (peerCount > 0) {
|
|
restoreTunnels(peerCount);
|
|
} else {
|
|
if (!_isIdle) {
|
|
if (_consec++ >= MAX_CONSEC_IDLE)
|
|
reduceTunnels();
|
|
else
|
|
restoreTunnels(1); // pretend we have one peer for now
|
|
}
|
|
}
|
|
} else {
|
|
_isIdle = false;
|
|
_consec = 0;
|
|
_consecNotRunning = 0;
|
|
_lastIn = "3";
|
|
_lastOut = "3";
|
|
}
|
|
schedule(CHECK_TIME);
|
|
}
|
|
|
|
/**
|
|
* Reduce to 1 in / 1 out tunnel
|
|
*/
|
|
private void reduceTunnels() {
|
|
_isIdle = true;
|
|
if (_log.shouldLog(Log.INFO))
|
|
_log.info("Reducing tunnels on idle");
|
|
setTunnels("1", "1", "0", "0");
|
|
}
|
|
|
|
/**
|
|
* Restore or adjust tunnel count based on current peer count
|
|
* @param peerCount greater than zero
|
|
*/
|
|
private void restoreTunnels(int peerCount) {
|
|
if (_isIdle && _log.shouldLog(Log.INFO))
|
|
_log.info("Restoring tunnels on activity");
|
|
_isIdle = false;
|
|
Map<String, String> opts = _util.getI2CPOptions();
|
|
String i = opts.get("inbound.quantity");
|
|
if (i == null)
|
|
i = "3";
|
|
String o = opts.get("outbound.quantity");
|
|
if (o == null)
|
|
o = "3";
|
|
String ib = opts.get("inbound.backupQuantity");
|
|
if (ib == null)
|
|
ib = "0";
|
|
String ob= opts.get("outbound.backupQuantity");
|
|
if (ob == null)
|
|
ob = "0";
|
|
// we don't need more tunnels than we have peers, reduce if so
|
|
// reduce to max(peerCount / 2, 2)
|
|
int in, out;
|
|
try {
|
|
in = Integer.parseInt(i);
|
|
} catch (NumberFormatException nfe) {
|
|
in = 3;
|
|
}
|
|
try {
|
|
out = Integer.parseInt(o);
|
|
} catch (NumberFormatException nfe) {
|
|
out = 3;
|
|
}
|
|
int target = Math.max(peerCount / 2, 2);
|
|
if (target < in && in > 2) {
|
|
in = target;
|
|
i = Integer.toString(in);
|
|
}
|
|
if (target < out && out > 2) {
|
|
out = target;
|
|
o = Integer.toString(out);
|
|
}
|
|
if (!(_lastIn.equals(i) && _lastOut.equals(o)))
|
|
setTunnels(i, o, ib, ob);
|
|
}
|
|
|
|
/**
|
|
* Set in / out / in backup / out backup tunnel counts
|
|
*/
|
|
private void setTunnels(String i, String o, String ib, String ob) {
|
|
_consec = 0;
|
|
I2PSocketManager mgr = _util.getSocketManager();
|
|
if (mgr != null) {
|
|
I2PSession sess = mgr.getSession();
|
|
if (sess != null) {
|
|
if (_log.shouldLog(Log.INFO))
|
|
_log.info("New tunnel settings " + i + " / " + o + " / " + ib + " / " + ob);
|
|
Properties newProps = new Properties();
|
|
newProps.setProperty("inbound.quantity", i);
|
|
newProps.setProperty("outbound.quantity", o);
|
|
newProps.setProperty("inbound.backupQuantity", ib);
|
|
newProps.setProperty("outbound.backupQuantity", ob);
|
|
sess.updateOptions(newProps);
|
|
_lastIn = i;
|
|
_lastOut = o;
|
|
}
|
|
}
|
|
}
|
|
}
|