* i2psnark:

- Add allocating and checking indications
   - Add bandwidth message at startup
   - More checks at torrent creation
This commit is contained in:
zzz
2012-10-06 13:41:50 +00:00
parent 0448537509
commit ddc750469c
5 changed files with 102 additions and 22 deletions

View File

@ -787,7 +787,8 @@ class PeerCoordinator implements PeerListener
} }
if (record) { if (record) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info(peer + " is now requesting: piece " + piece + " priority " + piece.getPriority()); _log.info("Now requesting from " + peer + ": piece " + piece + " priority " + piece.getPriority() +
" peers " + piece.getPeerCount() + '/' + peers.size());
piece.setRequested(peer, true); piece.setRequested(peer, true);
} }
return piece; return piece;

View File

@ -688,6 +688,22 @@ public class Snark
starting = true; starting = true;
} }
/**
* File checking in progress.
* @since 0.9.3
*/
public boolean isChecking() {
return storage != null && storage.isChecking();
}
/**
* Disk allocation (ballooning) in progress.
* @since 0.9.3
*/
public boolean isAllocating() {
return storage != null && storage.isAllocating();
}
/** /**
* @since 0.8.4 * @since 0.8.4
*/ */

View File

@ -707,7 +707,7 @@ public class SnarkManager implements Snark.CompleteListener {
public Properties getConfig() { return _config; } public Properties getConfig() { return _config; }
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */ /** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
private static final int MAX_FILES_PER_TORRENT = 512; public static final int MAX_FILES_PER_TORRENT = 512;
/** /**
* Set of canonical .torrent filenames that we are dealing with. * Set of canonical .torrent filenames that we are dealing with.
@ -1370,6 +1370,8 @@ public class SnarkManager implements Snark.CompleteListener {
} catch (Exception e) { } catch (Exception e) {
_log.error("Error in the DirectoryMonitor", e); _log.error("Error in the DirectoryMonitor", e);
} }
if (!_snarks.isEmpty())
addMessage(_("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
} }
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
} }

View File

@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.crypto.SHA1; import net.i2p.crypto.SHA1;
import net.i2p.data.ByteArray; import net.i2p.data.ByteArray;
@ -45,7 +46,7 @@ import net.i2p.util.SystemVersion;
*/ */
public class Storage public class Storage
{ {
private MetaInfo metainfo; private final MetaInfo metainfo;
private long[] lengths; private long[] lengths;
private RandomAccessFile[] rafs; private RandomAccessFile[] rafs;
private String[] names; private String[] names;
@ -69,6 +70,8 @@ public class Storage
private final int pieces; private final int pieces;
private final long total_length; private final long total_length;
private boolean changed; private boolean changed;
private volatile boolean _isChecking;
private final AtomicInteger _allocateCount = new AtomicInteger();
/** The default piece size. */ /** The default piece size. */
private static final int MIN_PIECE_SIZE = 256*1024; private static final int MIN_PIECE_SIZE = 256*1024;
@ -89,17 +92,15 @@ public class Storage
* Creates a new storage based on the supplied MetaInfo. This will * Creates a new storage based on the supplied MetaInfo. This will
* try to create and/or check all needed files in the MetaInfo. * try to create and/or check all needed files in the MetaInfo.
* *
* @exception IOException when creating and/or checking files fails. * Does not check storage. Caller MUST call check()
*/ */
public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener) public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
throws IOException
{ {
_util = util; _util = util;
_log = util.getContext().logManager().getLog(Storage.class); _log = util.getContext().logManager().getLog(Storage.class);
this.metainfo = metainfo; this.metainfo = metainfo;
this.listener = listener; this.listener = listener;
needed = metainfo.getPieces(); needed = metainfo.getPieces();
_probablyComplete = false;
bitfield = new BitField(needed); bitfield = new BitField(needed);
piece_size = metainfo.getPieceLength(0); piece_size = metainfo.getPieceLength(0);
pieces = needed; pieces = needed;
@ -107,12 +108,15 @@ public class Storage
} }
/** /**
* Creates a storage from the existing file or directory together * Creates a storage from the existing file or directory.
* with an appropriate MetaInfo file as can be announced on the * Creates an in-memory metainfo but does not save it to
* given announce String location. * a file, caller must do that.
*
* Creates the metainfo, this may take a LONG time. BLOCKING.
* *
* @param announce may be null * @param announce may be null
* @param listener may be null * @param listener may be null
* @throws IOException when creating and/or checking files fails.
*/ */
public Storage(I2PSnarkUtil util, File baseFile, String announce, public Storage(I2PSnarkUtil util, File baseFile, String announce,
boolean privateTorrent, StorageListener listener) boolean privateTorrent, StorageListener listener)
@ -135,6 +139,8 @@ public class Storage
if (total <= 0) if (total <= 0)
throw new IOException("Torrent contains no data"); throw new IOException("Torrent contains no data");
if (total > MAX_TOTAL_SIZE)
throw new IOException("Torrent too big (" + total + " bytes), max is " + MAX_TOTAL_SIZE);
int pc_size = MIN_PIECE_SIZE; int pc_size = MIN_PIECE_SIZE;
int pcs = (int) ((total - 1)/pc_size) + 1; int pcs = (int) ((total - 1)/pc_size) + 1;
@ -170,6 +176,7 @@ public class Storage
lengthsList = null; lengthsList = null;
} }
// TODO thread this so we can return and show something on the UI
byte[] piece_hashes = fast_digestCreate(); byte[] piece_hashes = fast_digestCreate();
metainfo = new MetaInfo(announce, baseFile.getName(), null, files, metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
lengthsList, piece_size, piece_hashes, total, privateTorrent); lengthsList, piece_size, piece_hashes, total, privateTorrent);
@ -205,6 +212,8 @@ public class Storage
private void getFiles(File base) throws IOException private void getFiles(File base) throws IOException
{ {
if (base.getAbsolutePath().equals("/"))
throw new IOException("Don't seed root");
ArrayList files = new ArrayList(); ArrayList files = new ArrayList();
addFiles(files, base); addFiles(files, base);
@ -233,12 +242,15 @@ public class Storage
} }
} }
private void addFiles(List l, File f) /**
{ * @throws IOException if too many total files
if (!f.isDirectory()) */
private void addFiles(List l, File f) throws IOException {
if (!f.isDirectory()) {
if (l.size() >= SnarkManager.MAX_FILES_PER_TORRENT)
throw new IOException("Too many files, limit is " + SnarkManager.MAX_FILES_PER_TORRENT + ", zip them?");
l.add(f); l.add(f);
else } else {
{
File[] files = f.listFiles(); File[] files = f.listFiles();
if (files == null) if (files == null)
{ {
@ -284,6 +296,23 @@ public class Storage
return changed; return changed;
} }
/**
* File checking in progress.
* @since 0.9.3
*/
public boolean isChecking() {
return _isChecking;
}
/**
* Disk allocation (ballooning) in progress.
* Always false on Windows.
* @since 0.9.3
*/
public boolean isAllocating() {
return _allocateCount.get() > 0;
}
/** /**
* @param file canonical path (non-directory) * @param file canonical path (non-directory)
* @return number of bytes remaining; -1 if unknown file * @return number of bytes remaining; -1 if unknown file
@ -703,11 +732,25 @@ public class Storage
* This is called at the beginning, and at presumed completion, * This is called at the beginning, and at presumed completion,
* so we have to be careful about locking. * so we have to be careful about locking.
* *
* TODO thread the checking so we can return and display
* something on the UI
*
* @param recheck if true, this is a check after we downloaded the * @param recheck if true, this is a check after we downloaded the
* last piece, and we don't modify the global bitfield unless * last piece, and we don't modify the global bitfield unless
* the check fails. * the check fails.
*/ */
private void checkCreateFiles(boolean recheck) throws IOException private void checkCreateFiles(boolean recheck) throws IOException {
synchronized(this) {
_isChecking = true;
try {
locked_checkCreateFiles(recheck);
} finally {
_isChecking = false;
}
}
}
private void locked_checkCreateFiles(boolean recheck) throws IOException
{ {
// Whether we are resuming or not, // Whether we are resuming or not,
// if any of the files already exists we assume we are resuming. // if any of the files already exists we assume we are resuming.
@ -867,11 +910,20 @@ public class Storage
final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024); final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
byte[] zeros = new byte[ZEROBLOCKSIZE]; byte[] zeros = new byte[ZEROBLOCKSIZE];
rafs[nr].seek(0); rafs[nr].seek(0);
// don't bother setting flag for small files
if (remaining > 20*1024*1024)
_allocateCount.incrementAndGet();
try {
while (remaining > 0) { while (remaining > 0) {
int size = (int) Math.min(remaining, ZEROBLOCKSIZE); int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
rafs[nr].write(zeros, 0, size); rafs[nr].write(zeros, 0, size);
remaining -= size; remaining -= size;
} }
} finally {
remaining = lengths[nr];
if (remaining > 20*1024*1024)
_allocateCount.decrementAndGet();
}
isSparse[nr] = false; isSparse[nr] = false;
} }

View File

@ -743,6 +743,7 @@ public class I2PSnarkServlet extends DefaultServlet {
try { try {
// This may take a long time to check the storage, but since it already exists, // This may take a long time to check the storage, but since it already exists,
// it shouldn't be THAT bad, so keep it in this thread. // it shouldn't be THAT bad, so keep it in this thread.
// TODO thread it for big torrents, perhaps a la FetchAndAdd
boolean isPrivate = _manager.getPrivateTrackers().contains(announceURL); boolean isPrivate = _manager.getPrivateTrackers().contains(announceURL);
Storage s = new Storage(_manager.util(), baseFile, announceURL, isPrivate, null); Storage s = new Storage(_manager.util(), baseFile, announceURL, isPrivate, null);
s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
@ -992,7 +993,13 @@ public class I2PSnarkServlet extends DefaultServlet {
String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd"); String rowClass = (row % 2 == 0 ? "snarkTorrentEven" : "snarkTorrentOdd");
String statusString; String statusString;
if (err != null) { if (snark.isChecking()) {
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Checking") + "\"></td>" +
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Checking");
} else if (snark.isAllocating()) {
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" title=\"" + _("Allocating") + "\"></td>" +
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Allocating");
} else if (err != null) {
if (isRunning && curPeers > 0 && !showPeers) if (isRunning && curPeers > 0 && !showPeers)
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" + statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td>" +
"<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") + "<td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
@ -1174,7 +1181,9 @@ public class I2PSnarkServlet extends DefaultServlet {
String b64 = Base64.encode(snark.getInfoHash()); String b64 = Base64.encode(snark.getInfoHash());
if (showPeers) if (showPeers)
parameters = parameters + "&p=1"; parameters = parameters + "&p=1";
if (isRunning) { if (snark.isChecking()) {
// show no buttons
} else if (isRunning) {
// Stop Button // Stop Button
if (isDegraded) if (isDegraded)
out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\""); out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\"");