Changeset d8b3d2c


Ignore:
Timestamp:
May 20, 2012 6:15:36 PM (8 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
6972d9d
Parents:
1da1dce
Message:
  • i2psnark:
    • Create sparse files at torrent creation and delay "ballooning" until first write (ticket #641)
    • Redo clear messages button
    • Concurrent message queue
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java

    r1da1dce rd8b3d2c  
    2020import java.util.StringTokenizer;
    2121import java.util.TreeMap;
     22import java.util.Queue;
    2223import java.util.concurrent.ConcurrentHashMap;
     24import java.util.concurrent.LinkedBlockingQueue;
    2325
    2426import net.i2p.I2PAppContext;
     
    5153    private final I2PAppContext _context;
    5254    private final Log _log;
    53     private final List<String> _messages;
     55    private final Queue<String> _messages;
    5456    private final I2PSnarkUtil _util;
    5557    private PeerCoordinatorSet _peerCoordinatorSet;
     
    124126        _context = I2PAppContext.getGlobalContext();
    125127        _log = _context.logManager().getLog(SnarkManager.class);
    126         _messages = new ArrayList(16);
     128        _messages = new LinkedBlockingQueue();
    127129        _util = new I2PSnarkUtil(_context);
    128130        _configFile = new File(CONFIG_FILE);
     
    155157    public I2PSnarkUtil util() { return _util; }
    156158
    157     private static final int MAX_MESSAGES = 5;
     159    private static final int MAX_MESSAGES = 100;
    158160
    159161    public void addMessage(String message) {
    160         synchronized (_messages) {
    161             _messages.add(message);
    162             while (_messages.size() > MAX_MESSAGES)
    163                 _messages.remove(0);
     162        _messages.offer(message);
     163        while (_messages.size() > MAX_MESSAGES) {
     164            _messages.poll();
    164165        }
    165166        if (_log.shouldLog(Log.INFO))
     
    169170    /** newest last */
    170171    public List<String> getMessages() {
    171         synchronized (_messages) {
    172             return new ArrayList(_messages);
    173         }
     172        if (_messages.isEmpty())
     173            return Collections.EMPTY_LIST;
     174        return new ArrayList(_messages);
    174175    }
    175176   
    176177    /** @since 0.9 */
    177178    public void clearMessages() {
    178         synchronized (_messages) {
    179179            _messages.clear();
    180         }
    181180    }
    182181   
     
    11931192            long delay = 60 * 1000 * getStartupDelayMinutes();
    11941193            if (delay > 0 && shouldAutoStart()) {
    1195                 _messages.add(_("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
     1194                addMessage(_("Adding torrents in {0}", DataHelper.formatDuration2(delay)));
    11961195                try { Thread.sleep(delay); } catch (InterruptedException ie) {}
    1197                 // the first message was a "We are starting up in 1m"
    1198                 synchronized (_messages) {
    1199                     if (_messages.size() == 1)
    1200                         _messages.remove(0);
    1201                 }
     1196                // Remove that first message
     1197                if (_messages.size() == 1)
     1198                    _messages.poll();
    12021199            }
    12031200
  • apps/i2psnark/java/src/org/klomp/snark/Storage.java

    r1da1dce rd8b3d2c  
    5151  /** priorities by file; default 0; may be null. @since 0.8.1 */
    5252  private int[] priorities;
     53  /** is the file empty and sparse? */
     54  private boolean[] isSparse;
    5355
    5456  private final StorageListener listener;
     
    7375
    7476  private static final Map<String, String> _filterNameCache = new ConcurrentHashMap();
     77
     78  private static final boolean _isWindows = System.getProperty("os.name").startsWith("Win");
    7579
    7680  /**
     
    203207    RAFfile = new File[size];
    204208    priorities = new int[size];
    205 
     209    isSparse = new boolean[size];
    206210
    207211    int i = 0;
     
    463467        RAFtime = new long[1];
    464468        RAFfile = new File[1];
     469        isSparse = new boolean[1];
    465470        lengths[0] = metainfo.getTotalLength();
    466471        RAFlock[0] = new Object();
     
    489494        RAFtime = new long[size];
    490495        RAFfile = new File[size];
     496        isSparse = new boolean[size];
    491497        for (int i = 0; i < size; i++)
    492498          {
     
    702708
    703709    // Make sure all files are available and of correct length
     710    // The files should all exist as they have been created with zero length by createFilesFromNames()
    704711    for (int i = 0; i < rafs.length; i++)
    705712      {
     
    726733          _util.debug(msg, Snark.ERROR);
    727734          changed = true;
     735          resume = true;
    728736          _probablyComplete = false; // to force RW
    729737          synchronized(RAFlock[i]) {
     
    799807  }
    800808
    801   /** this calls openRAF(); caller must synnchronize and call closeRAF() */
     809  /**
     810   *  This creates a (presumably) sparse file so that reads won't fail with IOE.
     811   *  Sets isSparse[nr] = true. balloonFile(nr) should be called later to
     812   *  defrag the file.
     813   *
     814   *  This calls openRAF(); caller must synchronize and call closeRAF().
     815   */
    802816  private void allocateFile(int nr) throws IOException
    803817  {
    804818    // caller synchronized
    805819    openRAF(nr, false);  // RW
    806     // XXX - Is this the best way to make sure we have enough space for
    807     // the whole file?
    808820    long remaining = lengths[nr];
    809821    if (listener != null)
    810822        listener.storageCreateFile(this, names[nr], remaining);
     823    rafs[nr].setLength(remaining);
     824    // don't bother ballooning later on Windows since there is no sparse file support
     825    // until JDK7 using the JSR-203 interface.
     826    // RAF seeks/writes do not create sparse files.
     827    // Windows will zero-fill up to the point of the write, which
     828    // will make the file fairly unfragmented, on average, at least until
     829    // near the end where it will get exponentially more fragmented.
     830    if (!_isWindows)
     831        isSparse[nr] = true;
     832    // caller will close rafs[nr]
     833    if (listener != null)
     834      listener.storageAllocated(this, lengths[nr]);
     835  }
     836
     837  /**
     838   *  This "balloons" the file with zeros to eliminate disk fragmentation.,
     839   *  Overwrites the entire file with zeros. Sets isSparse[nr] = false.
     840   *
     841   *  Caller must synchronize and call checkRAF() or openRAF().
     842   *  @since 0.9.1
     843   */
     844  private void balloonFile(int nr) throws IOException
     845  {
     846    _util.debug("Ballooning " + nr + ": " + RAFfile[nr], Snark.INFO);
     847    long remaining = lengths[nr];
    811848    final int ZEROBLOCKSIZE = (int) Math.min(remaining, 32*1024);
    812849    byte[] zeros = new byte[ZEROBLOCKSIZE];
     850    rafs[nr].seek(0);
    813851    while (remaining > 0) {
    814852        int size = (int) Math.min(remaining, ZEROBLOCKSIZE);
     
    816854        remaining -= size;
    817855    }
    818     // caller will close rafs[nr]
    819     if (listener != null)
    820       listener.storageAllocated(this, lengths[nr]);
     856    isSparse[nr] = false;
    821857  }
    822858
     
    910946              synchronized(RAFlock[i]) {
    911947                  checkRAF(i);
     948                  if (isSparse[i]) {
     949                      // If the file is a newly created sparse file,
     950                      // AND we aren't skipping it, balloon it with all
     951                      // zeros to un-sparse it by allocating the space.
     952                      // Obviously this could take a while.
     953                      // Once we have written to it, it isn't empty/sparse any more.
     954                      if (priorities == null || priorities[i] >= 0)
     955                          balloonFile(i);
     956                      else
     957                          isSparse[i] = false;
     958                  }
    912959                  rafs[i].seek(start);
    913960                  //rafs[i].write(bs, off + written, len);
     
    9591006
    9601007    return true;
    961   }
     1008 }
    9621009
    9631010  /**
  • apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java

    r1da1dce rd8b3d2c  
    298298        List<String> msgs = _manager.getMessages();
    299299        if (!msgs.isEmpty()) {
    300             out.write("<div class=\"snarkMessages\"><ul>");
    301             for (int i = msgs.size()-1; i >= 0; i--) {
    302                 String msg = msgs.get(i);
    303                 out.write("<li>" + msg + "</li>\n");
    304             }
    305             out.write("</ul><p><a href=\"/i2psnark/");
     300            out.write("<div class=\"snarkMessages\">");
     301            out.write("<a href=\"/i2psnark/");
    306302            if (isConfigure)
    307303                out.write("configure");
     
    310306            else
    311307                out.write("?");
    312             out.write("action=Clear&amp;nonce=" + _nonce + "\">" + _("clear messages") + "</a></p></div>");
     308            out.write("action=Clear&amp;nonce=" + _nonce + "\">" +
     309                      "<img src=\"" + _imgPath + "delete.png\" title=\"" + _("clear messages") +
     310                      "\" alt=\"" + _("clear messages") + "\"></a>" +
     311                      "<ul>");
     312            for (int i = msgs.size()-1; i >= 0; i--) {
     313                String msg = msgs.get(i);
     314                out.write("<li>" + msg + "</li>\n");
     315            }
     316            out.write("</ul></div>");
    313317        }
    314318    }
  • installer/resources/themes/snark/ubergine/snark.css

    r1da1dce rd8b3d2c  
    9494     overflow: auto;
    9595     color: #26f;
    96      max-height: 82px;
     96     max-height: 76px;
    9797     min-height: 45px;
    9898     width: auto;
     
    117117     margin: 0;
    118118     text-align: right;
     119}
     120
     121.snarkMessages img {
     122     float: right;
     123     margin: -3px -4px 4px 4px;
     124     opacity: 0.8;
    119125}
    120126
  • installer/resources/themes/snark/vanilla/snark.css

    r1da1dce rd8b3d2c  
    9393     overflow: auto;
    9494     color: #26f;
    95      max-height: 82px;
     95     max-height: 76px;
    9696     min-height: 45px;
    9797     width: auto;
     
    120120     margin: 0;
    121121     text-align: right;
     122}
     123
     124.snarkMessages img {
     125     float: right;
     126     margin: -3px -4px 4px 4px;
     127     opacity: 0.8;
    122128}
    123129
Note: See TracChangeset for help on using the changeset viewer.