/* * PeerCheckTasks - TimerTask that checks for good/bad up/downloaders. Copyright * (C) 2003 Mark J. Wielaard * * This file is part of Snark. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * * Revised by Stephen L. Reed, Dec 22, 2009. * Reformatted, fixed Checkstyle, Findbugs and PMD violations, and substituted Log4J logger * for consistency with the Texai project. */ package org.texai.torrent; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.TimerTask; import org.apache.log4j.Level; import org.apache.log4j.Logger; /** TimerTask that checks for good/bad up/downloader. Works together with the * PeerCoordinator to select which Peers get (un)choked. */ public final class PeerCheckerTask extends TimerTask { /** the logger */ private static final Logger LOGGER = Logger.getLogger(PeerCheckerTask.class); /** the kilo per second check period */ private static final long KILOPERSECOND = 1024 * (PeerCoordinator.CHECK_PERIOD / 1000); /** the peer coordinator */ private final PeerCoordinator coordinator; /** Constructs a new PeerCheckerTask instance. * * @param coordinator the peer coordinator */ PeerCheckerTask(final PeerCoordinator coordinator) { this.coordinator = coordinator; } /** Runs the peer checker task. */ @Override public void run() { synchronized (coordinator.getPeers()) { // Calculate total uploading and worst downloader. long wordDownloaded = Long.MAX_VALUE; Peer worstDownloadPeer = null; int uploaders = 0; int interested = 0; // Keep track of peers we remove now, // we will add them back to the end of the list. final List<Peer> removedPeers = new ArrayList<>(); final Iterator<Peer> peers_iter = coordinator.getPeers().iterator(); while (peers_iter.hasNext()) { final Peer peer = peers_iter.next(); // Remove dying peers if (!peer.isConnected()) { peers_iter.remove(); continue; } if (!peer.isChoking()) { uploaders++; } if (peer.isInterested()) { interested++; } // XXX - We should calculate the up/download rate a bit // more intelligently final long uploaded = peer.getUploaded(); final long downloaded = peer.getDownloaded(); peer.resetCounters(); LOGGER.log(Level.DEBUG, peer + ":" + " ul: " + uploaded / KILOPERSECOND + " dl: " + downloaded / KILOPERSECOND + " i: " + peer.isInterested() + " I: " + peer.isInteresting() + " c: " + peer.isChoking() + " C: " + peer.isChoked()); // If we are at our max uploaders and we have lots of other // interested peers try to make some room. // (Note use of coordinator.uploaders) if (coordinator.getUploaders() >= PeerCoordinator.MAX_UPLOADERS && interested > PeerCoordinator.MAX_UPLOADERS && !peer.isChoking()) { // Check if it still wants pieces from us. if (!peer.isInterested()) { // NOPMD LOGGER.log(Level.DEBUG, "Choke uninterested peer: " + peer); peer.setChoking(true); uploaders--; coordinator.setUploaders(coordinator.getUploaders() - 1); // Put it at the back of the list peers_iter.remove(); removedPeers.add(peer); } else if (peer.isChoked()) { // If they are choking us make someone else a downloader LOGGER.log(Level.DEBUG, "Choke choking peer: " + peer); peer.setChoking(true); uploaders--; coordinator.setUploaders(coordinator.getUploaders() - 1); // Put it at the back of the list peers_iter.remove(); removedPeers.add(peer); } else if (peer.isInteresting() && !peer.isChoked() && downloaded == 0) { // We are downloading but didn't receive anything... LOGGER.log(Level.DEBUG, "Choke downloader that doesn't deliver:" + peer); peer.setChoking(true); uploaders--; coordinator.setUploaders(coordinator.getUploaders() - 1); // Put it at the back of the list peers_iter.remove(); removedPeers.add(peer); } else if (!peer.isChoking() && downloaded < wordDownloaded) { // Make sure download is good if we are uploading wordDownloaded = downloaded; worstDownloadPeer = peer; } } } // Resync actual uploaders value // (can shift a bit by disconnecting peers) coordinator.setUploaders(uploaders); // Remove the worst downloader if needed. if (uploaders >= PeerCoordinator.MAX_UPLOADERS && interested > PeerCoordinator.MAX_UPLOADERS && worstDownloadPeer != null) { LOGGER.log(Level.DEBUG, "Choke worst downloader: " + worstDownloadPeer); worstDownloadPeer.setChoking(true); coordinator.setUploaders(coordinator.getUploaders() - 1); // Put it at the back of the list coordinator.getPeers().remove(worstDownloadPeer); removedPeers.add(worstDownloadPeer); } // Optimistically unchoke a peer coordinator.unchokePeer(); // Put peers back at the end of the list that we removed earlier. coordinator.getPeers().addAll(removedPeers); } } }