From 26c13b40fe25c5eb90db2bc88850ec7702d2fd95 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 13 Sep 2006 23:02:07 +0000 Subject: [PATCH] (zzz) * i2psnark: Mark a peer's requests as unrequested on disconnect, preventing premature end game * i2psnark: Randomize selection of next piece during end game * i2psnark: Don't restore a partial piece to a peer that is already working on it * i2psnark: strip ".torrent" on web page * i2psnark: Limit piece size in generated torrent to 1MB max --- .../java/src/org/klomp/snark/Peer.java | 4 +- .../src/org/klomp/snark/PeerCoordinator.java | 56 +++++++++++++++++++ .../src/org/klomp/snark/PeerListener.java | 8 +++ .../java/src/org/klomp/snark/PeerState.java | 46 +++++++++++++-- .../java/src/org/klomp/snark/Storage.java | 3 +- .../org/klomp/snark/web/I2PSnarkServlet.java | 19 ++++--- history.txt | 10 +++- .../src/net/i2p/router/RouterVersion.java | 4 +- 8 files changed, 133 insertions(+), 17 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index c1baaad8d2..39572fb3a8 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -321,8 +321,10 @@ public class Peer implements Comparable // try to save partial piece if (this.deregister) { PeerListener p = state.listener; - if (p != null) + if (p != null) { p.savePeerPartial(state); + p.markUnrequested(this); + } } state = null; diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index 7ae7f44b19..ced568f47c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -438,6 +438,8 @@ public class PeerCoordinator implements PeerListener //Only request a piece we've requested before if there's no other choice. if (piece == null) { + // let's not all get on the same piece + Collections.shuffle(requested); Iterator it2 = requested.iterator(); while (piece == null && it2.hasNext()) { @@ -701,5 +703,59 @@ public class PeerCoordinator implements PeerListener return null; } + /** Clear the requested flag for a piece if the peer + ** is the only one requesting it + */ + private void markUnrequestedIfOnlyOne(Peer peer, int piece) + { + // see if anybody else is requesting + synchronized (peers) + { + Iterator it = peers.iterator(); + while (it.hasNext()) { + Peer p = (Peer)it.next(); + if (p.equals(peer)) + continue; + if (p.state == null) + continue; + int[] arr = p.state.getRequestedPieces(); + for (int i = 0; arr[i] >= 0; i++) + if(arr[i] == piece) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Another peer is requesting piece " + piece); + return; + } + } + } + + // nobody is, so mark unrequested + synchronized(wantedPieces) + { + Iterator it = wantedPieces.iterator(); + while (it.hasNext()) { + Piece p = (Piece)it.next(); + if (p.getId() == piece) { + p.setRequested(false); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Removing from request list piece " + piece); + return; + } + } + } + } + + /** Mark a peer's requested pieces unrequested when it is disconnected + ** Once for each piece + ** This is enough trouble, maybe would be easier just to regenerate + ** the requested list from scratch instead. + */ + public void markUnrequested(Peer peer) + { + if (peer.state == null) + return; + int[] arr = peer.state.getRequestedPieces(); + for (int i = 0; arr[i] >= 0; i++) + markUnrequestedIfOnlyOne(peer, arr[i]); + } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java b/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java index 016e4442b7..0cdc153d25 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java @@ -160,4 +160,12 @@ public interface PeerListener * @return request (contains the partial data and valid length) */ Request getPeerPartial(BitField havePieces); + + /** Mark a peer's requested pieces unrequested when it is disconnected + * This prevents premature end game + * + * @param peer the peer that is disconnecting + */ + void markUnrequested(Peer peer); + } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java index 2e134704d4..a72d178b1c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java @@ -363,6 +363,31 @@ class PeerState return req; } + // return array of pieces terminated by -1 + // remove most duplicates + // but still could be some duplicates, not guaranteed + int[] getRequestedPieces() + { + int size = outstandingRequests.size(); + int[] arr = new int[size+2]; + int pc = -1; + int pos = 0; + if (pendingRequest != null) { + pc = pendingRequest.piece; + arr[pos++] = pc; + } + Request req = null; + for (int i = 0; i < size; i++) { + Request r1 = (Request)outstandingRequests.get(i); + if (pc != r1.piece) { + pc = r1.piece; + arr[pos++] = pc; + } + } + arr[pos] = -1; + return(arr); + } + void cancelMessage(int piece, int begin, int length) { if (_log.shouldLog(Log.DEBUG)) @@ -498,11 +523,22 @@ class PeerState // Check for adopting an orphaned partial piece Request r = listener.getPeerPartial(bitfield); if (r != null) { - outstandingRequests.add(r); - if (!choked) - out.sendRequest(r); - lastRequest = r; - return true; + // Check that r not already in outstandingRequests + int[] arr = getRequestedPieces(); + boolean found = false; + for (int i = 0; arr[i] >= 0; i++) { + if (arr[i] == r.piece) { + found = true; + break; + } + } + if (found) { + outstandingRequests.add(r); + if (!choked) + out.sendRequest(r); + lastRequest = r; + return true; + } } int nextPiece = listener.wantPiece(peer, bitfield); if (_log.shouldLog(Log.DEBUG)) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index 1ce0d552cf..c7d1eb94e3 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -48,6 +48,7 @@ public class Storage /** The default piece size. */ private static int MIN_PIECE_SIZE = 256*1024; + private static int MAX_PIECE_SIZE = 1024*1024; /** The maximum number of pieces in a torrent. */ private static long MAX_PIECES = 100*1024/20; @@ -90,7 +91,7 @@ public class Storage piece_size = MIN_PIECE_SIZE; pieces = (int) ((total - 1)/piece_size) + 1; - while (pieces > MAX_PIECES) + while (pieces > MAX_PIECES && piece_size < MAX_PIECE_SIZE) { piece_size = piece_size*2; pieces = (int) ((total - 1)/piece_size) +1; diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index e58ad46d89..18b7ce460c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -294,6 +294,9 @@ public class I2PSnarkServlet extends HttpServlet { String filename = snark.torrent; File f = new File(filename); filename = f.getName(); // the torrent may be the canonical name, so lets just grab the local name + int i = filename.lastIndexOf(".torrent"); + if (i > 0) + filename = filename.substring(0, i); if (filename.length() > MAX_DISPLAYED_FILENAME_LENGTH) filename = filename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH) + "..."; long total = snark.meta.getTotalLength(); @@ -304,17 +307,19 @@ public class I2PSnarkServlet extends HttpServlet { long downBps = snark.coordinator.getDownloadRate(); long upBps = snark.coordinator.getUploadRate(); long remainingSeconds; - if (downBps > 0) - remainingSeconds = remaining / downBps; - else - remainingSeconds = -1; + if (downBps > 0) + remainingSeconds = remaining / downBps; + else + remainingSeconds = -1; long uploaded = snark.coordinator.getUploaded(); + boolean isRunning = !snark.stopped; stats[0] += snark.coordinator.getDownloaded(); stats[1] += uploaded; - stats[2] += downBps; - stats[3] += upBps; + if (isRunning) { + stats[2] += downBps; + stats[3] += upBps; + } - boolean isRunning = !snark.stopped; boolean isValid = snark.meta != null; boolean singleFile = snark.meta.getFiles() == null; diff --git a/history.txt b/history.txt index 6e0afcdfaa..17928829fe 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,12 @@ -$Id: history.txt,v 1.517 2006-09-09 17:15:06 zzz Exp $ +$Id: history.txt,v 1.518 2006-09-09 20:55:38 zzz Exp $ + +2006-09-10 zzz + * i2psnark: Mark a peer's requests as unrequested on disconnect, + preventing premature end game + * i2psnark: Randomize selection of next piece during end game + * i2psnark: Don't restore a partial piece to a peer that is already working on it + * i2psnark: strip ".torrent" on web page + * i2psnark: Limit piece size in generated torrent to 1MB max 2006-09-09 zzz * i2psnark: Add "Stalled" indication and stat totals on web page diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 47dd841fa3..3021eab554 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.455 $ $Date: 2006-09-09 17:15:05 $"; + public final static String ID = "$Revision: 1.456 $ $Date: 2006-09-09 20:55:37 $"; public final static String VERSION = "0.6.1.25"; - public final static long BUILD = 2; + public final static long BUILD = 3; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID);