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("&nbsp;");
+                toThemeImg(buf, "head_rx");
+                buf.append("&nbsp;<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;