/*
* 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.
*/
package org.klomp.snark;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* TimerTask that checks for good/bad up/downloader. Works together with the
* PeerCoordinator to select which Peers get (un)choked.
*/
class PeerCheckerTask extends TimerTask
{
private static final long KILOPERSECOND = 1024 * (PeerCoordinator.CHECK_PERIOD / 1000);
private final PeerCoordinator coordinator;
PeerCheckerTask (PeerCoordinator coordinator)
{
this.coordinator = coordinator;
}
@Override
public void run ()
{
synchronized (coordinator.peers) {
// Calculate total uploading and worst downloader.
long worstdownload = Long.MAX_VALUE;
Peer worstDownloader = null;
int peers = 0;
int uploaders = 0;
int downloaders = 0;
int interested = 0;
int interesting = 0;
int choking = 0;
int choked = 0;
long uploaded = 0;
long downloaded = 0;
// Keep track of peers we remove now,
// we will add them back to the end of the list.
List<Peer> removed = new ArrayList<Peer>();
Iterator it = coordinator.peers.iterator();
while (it.hasNext()) {
Peer peer = (Peer)it.next();
// Remove dying peers
if (!peer.isConnected()) {
it.remove();
continue;
}
peers++;
if (!peer.isChoking()) {
uploaders++;
}
if (!peer.isChoked() && peer.isInteresting()) {
downloaders++;
}
if (peer.isInterested()) {
interested++;
}
if (peer.isInteresting()) {
interesting++;
}
if (peer.isChoking()) {
choking++;
}
if (peer.isChoked()) {
choked++;
}
// XXX - We should calculate the up/download rate a bit
// more intelligently
long upload = peer.getUploaded();
uploaded += upload;
long download = peer.getDownloaded();
downloaded += download;
peer.resetCounters();
log.log(Level.FINEST, peer + ":" + " ul: " + upload
/ KILOPERSECOND + " dl: " + download / 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.uploaders >= PeerCoordinator.MAX_UPLOADERS
&& interested > PeerCoordinator.MAX_UPLOADERS
&& !peer.isChoking()) {
// Check if it still wants pieces from us.
if (!peer.isInterested()) {
log.log(Level.FINER, "Choke uninterested peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
// Put it at the back of the list
it.remove();
removed.add(peer);
} else if (peer.isChoked()) {
// If they are choking us make someone else a downloader
log.log(Level.FINEST, "Choke choking peer: " + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
// Put it at the back of the list
it.remove();
removed.add(peer);
} else if (peer.isInteresting() && !peer.isChoked()
&& download == 0) {
// We are downloading but didn't receive anything...
log.log(Level.FINEST,
"Choke downloader that doesn't deliver:" + peer);
peer.setChoking(true);
uploaders--;
coordinator.uploaders--;
// Put it at the back of the list
it.remove();
removed.add(peer);
} else if (!peer.isChoking() && download < worstdownload) {
// Make sure download is good if we are uploading
worstdownload = download;
worstDownloader = peer;
}
}
}
// Resync actual uploaders value
// (can shift a bit by disconnecting peers)
coordinator.uploaders = uploaders;
// Remove the worst downloader if needed.
if (uploaders >= PeerCoordinator.MAX_UPLOADERS
&& interested > PeerCoordinator.MAX_UPLOADERS
&& worstDownloader != null) {
log.log(Level.FINEST, "Choke worst downloader: "
+ worstDownloader);
worstDownloader.setChoking(true);
coordinator.uploaders--;
// Put it at the back of the list
coordinator.peers.remove(worstDownloader);
removed.add(worstDownloader);
}
// Optimistically unchoke a peer
coordinator.unchokePeer();
// Put peers back at the end of the list that we removed earlier.
coordinator.peers.addAll(removed);
}
}
protected static final Logger log = Logger.getLogger("org.klomp.snark.peer");
}