diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index cba955f691da30bf5f7721485a9488651f1911bf..fbbb1859af47a8a11d6a5f17fcd103e36a126814 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -62,7 +62,7 @@ public class Peer implements Comparable<Peer> // Keeps state for in/out connections. Non-null when the handshake // was successful, the connection setup and runs - PeerState state; + volatile PeerState state; /** shared across all peers on this torrent */ MagnetState magnetState; diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionIn.java b/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionIn.java index 1f47b59b6056c00addf57a7d435e5dfef3fd923f..da1b943332f8e4c38664c9fd9dbe393deee89b85 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionIn.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerConnectionIn.java @@ -39,7 +39,7 @@ class PeerConnectionIn implements Runnable private static final int MAX_MSG_SIZE = Math.max(PeerState.PARTSIZE + 9, MagnetState.CHUNK_SIZE + 100); // 100 for the ext msg dictionary - private Thread thread; + private volatile Thread thread; private volatile boolean quit; long lastRcvd; @@ -75,9 +75,12 @@ class PeerConnectionIn implements Runnable thread = Thread.currentThread(); try { - PeerState ps = peer.state; - while (!quit && ps != null) + while (!quit) { + final PeerState ps = peer.state; + if (ps == null) + break; + // Common variables used for some messages. int piece; int begin; @@ -91,9 +94,9 @@ class PeerConnectionIn implements Runnable if (i == 0) { - ps.keepAliveMessage(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received keepalive from " + peer); + ps.keepAliveMessage(); continue; } @@ -101,51 +104,51 @@ class PeerConnectionIn implements Runnable switch (b) { case Message.CHOKE: - ps.chokeMessage(true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received choke from " + peer); + ps.chokeMessage(true); break; case Message.UNCHOKE: - ps.chokeMessage(false); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received unchoke from " + peer); + ps.chokeMessage(false); break; case Message.INTERESTED: - ps.interestedMessage(true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received interested from " + peer); + ps.interestedMessage(true); break; case Message.UNINTERESTED: - ps.interestedMessage(false); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received not interested from " + peer); + ps.interestedMessage(false); break; case Message.HAVE: piece = din.readInt(); - ps.haveMessage(piece); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received havePiece(" + piece + ") from " + peer); + ps.haveMessage(piece); break; case Message.BITFIELD: byte[] bitmap = new byte[i-1]; din.readFully(bitmap); - ps.bitfieldMessage(bitmap); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received bitmap from " + peer + ": size=" + (i-1) /* + ": " + ps.bitfield */ ); + ps.bitfieldMessage(bitmap); break; case Message.REQUEST: piece = din.readInt(); begin = din.readInt(); len = din.readInt(); - ps.requestMessage(piece, begin, len); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received request(" + piece + "," + begin + ") from " + peer); + ps.requestMessage(piece, begin, len); break; case Message.PIECE: @@ -156,9 +159,9 @@ class PeerConnectionIn implements Runnable if (req != null) { req.read(din); - ps.pieceMessage(req); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received data(" + piece + "," + begin + ") from " + peer); + ps.pieceMessage(req); } else { @@ -175,16 +178,16 @@ class PeerConnectionIn implements Runnable piece = din.readInt(); begin = din.readInt(); len = din.readInt(); - ps.cancelMessage(piece, begin, len); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received cancel(" + piece + "," + begin + ") from " + peer); + ps.cancelMessage(piece, begin, len); break; case Message.PORT: int port = din.readUnsignedShort(); - ps.portMessage(port); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received port message from " + peer); + ps.portMessage(port); break; case Message.EXTENSION: @@ -247,11 +250,9 @@ class PeerConnectionIn implements Runnable if (_log.shouldLog(Log.INFO)) _log.info("IOError talking with " + peer, ioe); } - catch (Throwable t) + catch (RuntimeException t) { _log.error("Error talking with " + peer, t); - if (t instanceof OutOfMemoryError) - throw (OutOfMemoryError)t; } finally { diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index f8dadb627fd51730a8ff3a71504b5f36bc21b707..b0f9833136b37f219e4dfcd7c7f95c9fdb850d98 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -911,6 +911,30 @@ public class Snark return -1; } + /** + * Bytes not received and set to skipped. + * This is not the same as the total of all skipped files, + * since pieces may span multiple files. + * + * @return exact value. or 0 if no storage yet. + * @since 0.9.24 + */ + public long getSkippedLength() { + PeerCoordinator coord = coordinator; + if (coord != null) { + // fast way + long r = getRemainingLength(); + if (r <= 0) + return 0; + long n = coord.getNeededLength(); + return r - n; + } else if (storage != null) { + // slow way + return storage.getSkippedLength(); + } + return 0; + } + /** * Does not account (i.e. includes) for skipped files. * @return number of pieces still needed (magnet mode or not), or -1 if unknown diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index f8771ded3e075e162154ab03ceb3d9cdb14e0701..acb5255d54645cacfd6df7ca4ee18cdb0127f5e7 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -518,6 +518,31 @@ public class Storage implements Closeable return rv; } + /** + * Call setPriority() for all changed files first, + * then call this. + * The length of all the pieces that are not yet downloaded, + * and are set to skipped. + * This is not the same as the total of all skipped files, + * since pieces may span multiple files. + * + * @return 0 on error, if complete, or if only one file + * @since 0.9.24 + */ + public long getSkippedLength() { + int[] pri = getPiecePriorities(); + if (pri == null) + return 0; + long rv = 0; + final int end = pri.length - 1; + for (int i = 0; i <= end; i++) { + if (pri[i] <= -9 && !bitfield.get(i)) { + rv += (i != end) ? piece_size : metainfo.getPieceLength(i); + } + } + return rv; + } + /** * The BitField that tells which pieces this storage contains. * Do not change this since this is the current state of the storage. 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 53897366023c23e6e93e5bce4b91eb9032903e8f..4d7727729b4be3396a257917361b66242733f4d5 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -2916,6 +2916,15 @@ public class I2PSnarkServlet extends BasicServlet { .append(":</b> ") .append(formatSize(needed)); } + long skipped = snark.getSkippedLength(); + if (skipped > 0) { + buf.append(" "); + toThemeImg(buf, "head_rx"); + buf.append(" <b>") + .append(_t("Skipped")) + .append(":</b> ") + .append(formatSize(skipped)); + } if (meta != null) { List<List<String>> files = meta.getFiles(); int fileCount = files != null ? files.size() : 1;