diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
index 5ebad97889e328d2b6141b5d2eb6e288aa8d0af9..2bf4f2d41de6af92d143e9015104dc8ca01e9353 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
@@ -55,8 +55,9 @@ public class I2PSnarkUtil {
     private String _i2cpHost;
     private int _i2cpPort;
     private final Map<String, String> _opts;
-    private I2PSocketManager _manager;
+    private volatile I2PSocketManager _manager;
     private boolean _configured;
+    private volatile boolean _connecting;
     private final Set<Hash> _shitlist;
     private int _maxUploaders;
     private int _maxUpBW;
@@ -198,6 +199,7 @@ public class I2PSnarkUtil {
      */
     synchronized public boolean connect() {
         if (_manager == null) {
+            _connecting = true;
             // try to find why reconnecting after stop
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Connecting to I2P", new Exception("I did it"));
@@ -237,6 +239,7 @@ public class I2PSnarkUtil {
             if (opts.getProperty("i2p.streaming.maxConnsPerHour") == null)
                 opts.setProperty("i2p.streaming.maxConnsPerHour", "20");
             _manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
+            _connecting = false;
         }
         // FIXME this only instantiates krpc once, left stuck with old manager
         //if (ENABLE_DHT && _manager != null && _dht == null)
@@ -252,6 +255,9 @@ public class I2PSnarkUtil {
 
     public boolean connected() { return _manager != null; }
 
+    /** @since 0.9.1 */
+    public boolean isConnecting() { return _manager == null && _connecting; }
+
     /**
      *  For FetchAndAdd
      *  @return null if not connected
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
index 682fcf35e7a113aba3c1d8a1585aa27836021433..acf5f15ed5796b9ef2ab4bf9e33cd1a3fc5bccca 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java
@@ -250,6 +250,7 @@ public class Snark
   private String rootDataDir = ".";
   private final CompleteListener completeListener;
   private boolean stopped;
+  private boolean starting;
   private byte[] id;
   private byte[] infoHash;
   private String additionalTrackerURL;
@@ -509,9 +510,19 @@ public class Snark
   }
 
   /**
-   * Start up contacting peers and querying the tracker
+   * Start up contacting peers and querying the tracker.
+   * Blocks if tunnel is not yet open.
    */
-  public void startTorrent() {
+  public synchronized void startTorrent() {
+      starting = true;
+      try {
+          x_startTorrent();
+      } finally {
+          starting = false;
+      }
+  }
+
+  private void x_startTorrent() {
     boolean ok = _util.connect();
     if (!ok) fatal("Unable to connect to I2P");
     if (coordinator == null) {
@@ -585,7 +596,7 @@ public class Snark
    * @param fast if true, limit the life of the unannounce threads
    * @since 0.9.1
    */
-  public void stopTorrent(boolean fast) {
+  public synchronized void stopTorrent(boolean fast) {
     stopped = true;
     TrackerClient tc = trackerclient;
     if (tc != null)
@@ -680,6 +691,22 @@ public class Snark
         return stopped;
     }
 
+    /**
+     *  Startup in progress.
+     *  @since 0.9.1
+     */
+    public boolean isStarting() {
+        return starting && stopped;
+    }
+
+    /**
+     *  Set startup in progress.
+     *  @since 0.9.1
+     */
+    public void setStarting() {
+        starting = true;
+    }
+
     /**
      *  @since 0.8.4
      */
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index 5c70cd0ea78b84e658d051b1f392ca9f25105f48..f79c13b815c12a016acb55d5ce657d690c48fa17 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -867,7 +867,7 @@ public class SnarkManager implements Snark.CompleteListener {
             torrent.startTorrent();
             addMessage(_("Fetching {0}", name));
             boolean haveSavedPeers = false;
-            if ((!util().connected()) && !haveSavedPeers) {
+            if ((_util.connected()) && !haveSavedPeers) {
                 addMessage(_("We have no saved peers and no other torrents are running. " +
                              "Fetch of {0} will not succeed until you start another torrent.", name));
             }
@@ -1599,6 +1599,81 @@ public class SnarkManager implements Snark.CompleteListener {
         }
     }
 
+    /**
+     *  If not connected, thread it, otherwise inline
+     *  @since 0.9.1
+     */
+    public void startTorrent(byte[] infoHash) {
+        for (Snark snark : _snarks.values()) {
+            if (DataHelper.eq(infoHash, snark.getInfoHash())) {
+                if (snark.isStarting() || !snark.isStopped()) {
+                    addMessage("Torrent already started");
+                    return;
+                }
+                boolean connected = _util.connected();
+                if ((!connected) && !_util.isConnecting())
+                    addMessage(_("Opening the I2P tunnel"));
+                addMessage(_("Starting up torrent {0}", snark.getBaseName()));
+                if (connected) {
+                    snark.startTorrent();
+                } else {
+                    // mark it for the UI
+                    snark.setStarting();
+                    (new I2PAppThread(new ThreadedStarter(snark), "TorrentStarter", true)).start();
+                    try { Thread.sleep(200); } catch (InterruptedException ie) {}
+                }
+                return;
+            }
+        }
+        addMessage("Torrent not found?");
+    }
+
+    /**
+     *  If not connected, thread it, otherwise inline
+     *  @since 0.9.1
+     */
+    public void startAllTorrents() {
+        if (_util.connected()) {
+            startAll();
+        } else {
+            addMessage(_("Opening the I2P tunnel and starting all torrents."));
+            for (Snark snark : _snarks.values()) {
+                // mark it for the UI
+                snark.setStarting();
+            }
+            (new I2PAppThread(new ThreadedStarter(null), "TorrentStarterAll", true)).start();
+            try { Thread.sleep(200); } catch (InterruptedException ie) {}
+        }
+    }
+
+    /**
+     *  Use null constructor param for all
+     *  @since 0.9.1
+     */
+    private class ThreadedStarter implements Runnable {
+        private final Snark snark;
+        public ThreadedStarter(Snark s) { snark = s; }
+        public void run() {
+            if (snark != null) {
+                if (snark.isStopped())
+                    snark.startTorrent();
+            } else {
+                startAll();
+            }
+        }
+    }
+
+    /**
+     *  Inline
+     *  @since 0.9.1
+     */
+    private void startAll() {
+        for (Snark snark : _snarks.values()) {
+            if (snark.isStopped())
+                snark.startTorrent();
+        }
+    }
+
     /**
      * Stop all running torrents, and close the tunnel after a delay
      * to allow for announces.
@@ -1631,7 +1706,7 @@ public class SnarkManager implements Snark.CompleteListener {
                 // Schedule this even for final shutdown, as there's a chance
                 // that it's just this webapp that is stopping.
                 SimpleScheduler.getInstance().addEvent(new Disconnector(), 60*1000);
-                addMessage(_("Closing I2P tunnel after announces to trackers."));
+                addMessage(_("Closing I2P tunnel after notifying trackers."));
                 if (finalShutdown) {
                     try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
                 }
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 fbc6cb18d88067d72734b0439778a23d8cc90e23..8a018e59f8f7d53798ea38edf9d2df84d85e8ebe 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -422,7 +422,7 @@ public class I2PSnarkServlet extends DefaultServlet {
             out.write("\">");
             if (isDegraded)
                 out.write("</a>");
-        } else if (!snarks.isEmpty()) {
+        } else if ((!_manager.util().isConnecting()) && !snarks.isEmpty()) {
             if (isDegraded)
                 out.write("<a href=\"/i2psnark/?action=StartAll&amp;nonce=" + _nonce + "\"><img title=\"");
             else
@@ -573,14 +573,7 @@ public class I2PSnarkServlet extends DefaultServlet {
             if (torrent != null) {
                 byte infoHash[] = Base64.decode(torrent);
                 if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
-                    for (String name : _manager.listTorrentFiles()) {
-                        Snark snark = _manager.getTorrent(name);
-                        if ( (snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash())) ) {
-                            snark.startTorrent();
-                            _manager.addMessage(_("Starting up torrent {0}", snark.getBaseName()));
-                            break;
-                        }
-                    }
+                    _manager.startTorrent(infoHash);
                 }
             }
         } else if (action.startsWith("Remove_")) {
@@ -747,13 +740,7 @@ public class I2PSnarkServlet extends DefaultServlet {
         } else if ("StopAll".equals(action)) {
             _manager.stopAllTorrents(false);
         } else if ("StartAll".equals(action)) {
-            _manager.addMessage(_("Opening the I2P tunnel and starting all torrents."));
-            List<Snark> snarks = getSortedSnarks(req);
-            for (int i = 0; i < snarks.size(); i++) {
-                Snark snark = snarks.get(i);
-                if (snark.isStopped())
-                    snark.startTorrent();
-            }
+            _manager.startAllTorrents();
         } else if ("Clear".equals(action)) {
             _manager.clearMessages();
         } else {
@@ -989,6 +976,8 @@ public class I2PSnarkServlet extends DefaultServlet {
                 statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
                 "<br>" + err;
             }
+        } else if (snark.isStarting()) {
+            statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Starting");
         } else if (remaining == 0 || needed == 0) {  // < 0 means no meta size yet
             // partial complete or seeding
             if (isRunning) {
@@ -1141,6 +1130,7 @@ public class I2PSnarkServlet extends DefaultServlet {
         if (showPeers)
             parameters = parameters + "&p=1";
         if (isRunning) {
+            // Stop Button
             if (isDegraded)
                 out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\"");
             else
@@ -1151,7 +1141,8 @@ public class I2PSnarkServlet extends DefaultServlet {
             out.write("\">");
             if (isDegraded)
                 out.write("</a>");
-        } else {
+        } else if (!snark.isStarting()) {
+                // Start Button
                 // This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
                 if (noThinsp)
                     out.write("<a href=\"/i2psnark/?action=Start_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\"");
@@ -1165,6 +1156,7 @@ public class I2PSnarkServlet extends DefaultServlet {
                     out.write("</a>");
 
             if (isValid) {
+                // Remove Button
                 // Doesnt work with Opera so use noThinsp instead of isDegraded
                 if (noThinsp)
                     out.write("<a href=\"/i2psnark/?action=Remove_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\"");
@@ -1184,6 +1176,7 @@ public class I2PSnarkServlet extends DefaultServlet {
                     out.write("</a>");
             }
 
+            // Delete Button
             // Doesnt work with Opera so use noThinsp instead of isDegraded
             if (noThinsp)
                 out.write("<a href=\"/i2psnark/?action=Delete_" + b64 + "&amp;nonce=" + _nonce + "\"><img title=\"");