package com.limegroup.bittorrent;
import java.io.File;
import java.util.Date;
import org.limewire.bittorrent.Torrent;
import org.limewire.bittorrent.TorrentManager;
import org.limewire.bittorrent.TorrentPeer;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import com.google.inject.Inject;
/**
* The dht scheduler will check the torrents every minute to see if they may
* need the dht to continue downloading.
*
* If a torrent does need the dht and the dht is not started, it will be
* started. Every five minutes there after the torrent will be checked to see if
* it still needs the dht. If it does not, then that torrent will be marked as
* not needing the dht.
*
* If there are no torrents needing the dht then it will be shutdown.
*/
public class TorrentDHTScheduler implements Runnable {
private static final Log LOG = LogFactory.getLog(TorrentDHTScheduler.class);
private static final String LAST_DHT_START_TIME = "lastDHTStartTime";
private static final String DHT_ENABLED = "dhtEnabled";
private static final long ONE_MINUTE = 60 * 1000;
private static final long ONE_HOUR = ONE_MINUTE * 60;
private final TorrentManager torrentManager;
@Inject
public TorrentDHTScheduler(TorrentManager torrentManager) {
this.torrentManager = torrentManager;
}
@Override
public void run() {
if (LOG.isDebugEnabled()) {
LOG.debugf("Running DHT Checks: {0}", new Date());
}
torrentManager.getLock().lock();
try {
boolean dhtStarted = torrentManager.isDHTStarted();
boolean dhtEnabled = false;
for (Torrent torrent : torrentManager.getTorrents()) {
if (dhtEnabled(torrent) && shouldDisableDHT(torrent)) {
torrent.setProperty(DHT_ENABLED, Boolean.FALSE);
dhtEnabled |= false;
LOG.debugf("Disabling DHT for torrent: {0}", torrent.getName());
} else if (needsDHTConnections(torrent) && shouldTryDHT(torrent)) {
torrent.setProperty(LAST_DHT_START_TIME, new Long(System.currentTimeMillis()));
torrent.setProperty(DHT_ENABLED, Boolean.TRUE);
dhtEnabled |= true;
LOG.debugf("Enabling DHT for torrent: {0}", torrent.getName());
} else if (dhtEnabled(torrent)) {
dhtEnabled |= true;
LOG.debugf("Keeping DHT Enabled for torrent: {0}", torrent.getName());
} else {
LOG.debugf("Keeping DHT Disabled for torrent: {0}", torrent.getName());
}
}
File dhtStateFile = torrentManager.getTorrentManagerSettings().getDHTStateFile();
if (!dhtStarted && dhtEnabled) {
torrentManager.startDHT(dhtStateFile);
LOG.debugf("DHT Started");
// TODO this will currently not work, starting a dht after a
// torrent has already been added
// does not currently attach the dht to the torrent, in
// 0.14.7
// this behavior will be updated
// to attach the dht to already running torrents. we can remove
// this comment then.
// or we can add the fix into our own build once it becomes
// available.
} else if (dhtStarted && !dhtEnabled) {
torrentManager.saveDHTState(dhtStateFile);
torrentManager.stopDHT();
LOG.debugf("DHT Stopped");
} else {
if (LOG.isDebugEnabled()) {
LOG.debugf("DHT Kept: {0}", (dhtStarted ? "on" : "off"));
}
}
} finally {
torrentManager.getLock().unlock();
}
}
private boolean dhtEnabled(Torrent torrent) {
Boolean dhtEnabled = torrent.getProperty(DHT_ENABLED, Boolean.FALSE);
return dhtEnabled.booleanValue();
}
private boolean shouldDisableDHT(Torrent torrent) {
long runningTimeOfTorrent = getTorrentRunningTime(torrent);
if (!torrent.isStarted() || torrent.isFinished() || torrent.isPrivate()
|| runningTimeOfTorrent < ONE_MINUTE) {
return true;
}
Long lastDHTStartTime = torrent.getProperty(LAST_DHT_START_TIME, null);
if (dhtEnabled(torrent) && lastDHTStartTime != null) {
long runningTimeMillis = getTimeSinceLastDHTStartForTorrent(lastDHTStartTime);
long runningTimeMinutes = runningTimeMillis / (60 * 1000);
if (LOG.isDebugEnabled()) {
LOG.debugf("DHT Running for torrent: {0} for {1} minutes.", torrent.getName(),
runningTimeMinutes);
}
if (runningTimeMinutes > 30) {
// only try the dht for 30 minutes at a time
return true;
}
// check the number of peers every five minutes max.
if (runningTimeMinutes % 5 == 0) {
int numDHTNonTrackerPeers = 0;
int numTotalPeers = 0;
int numNonDHTPeers = 0;
for (TorrentPeer peer : torrent.getTorrentPeers()) {
numTotalPeers++;
if (peer.isFromDHT() && !peer.isFromTracker()) {
numDHTNonTrackerPeers++;
} else {
numNonDHTPeers++;
}
}
if (LOG.isDebugEnabled()) {
LOG.debugf("Torrent: {0} has {1} total peers.", torrent.getName(),
numTotalPeers);
LOG.debugf("Torrent: {0} has {1} DHT non tracker peers.", torrent.getName(),
numDHTNonTrackerPeers);
LOG.debugf("Torrent: {0} has {1} non DHT peers.", torrent.getName(),
numNonDHTPeers);
}
return numTotalPeers > 0 && (numNonDHTPeers / (float) numTotalPeers) > .50;
}
}
return false;
}
private boolean shouldTryDHT(Torrent torrent) {
Boolean dhtEnabled = torrent.getProperty(DHT_ENABLED, null);
Long lastDHTStartTime = torrent.getProperty(LAST_DHT_START_TIME, null);
if (dhtEnabled == null || lastDHTStartTime == null) {
return true;
}
long timeSinceLastDHTStart = getTimeSinceLastDHTStartForTorrent(lastDHTStartTime);
return timeSinceLastDHTStart > ONE_HOUR;
}
private boolean needsDHTConnections(Torrent torrent) {
if (!torrent.isPaused()) {
return torrent.getNumConnections() < 5;
}
return false;
}
/**
* Returns the time in milliseconds that the dht was last enabled for this
* torrent.
*/
private long getTimeSinceLastDHTStartForTorrent(Long lastDHTStartTime) {
long timeSinceLastDHTStart = System.currentTimeMillis() - lastDHTStartTime.longValue();
return timeSinceLastDHTStart;
}
/**
* Returns the torrents running time in milliseconds.
*/
private long getTorrentRunningTime(Torrent torrent) {
long runningTimeOfTorrent = System.currentTimeMillis() - torrent.getStartTime();
return runningTimeOfTorrent;
}
}