From 7bb792836df8d2db8c50488d55eb6f7094fecffd Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Thu, 23 Feb 2006 01:48:47 +0000
Subject: [PATCH] 2006-02-22  jrandom     * Fix to properly profile tunnel
 joins (thanks Ragnarok, frosk, et al!)     * More aggressive poor-man's PMTU,
 allowing larger MTUs on less reliable       links     * Further class
 validator refactorings

---
 router/java/src/net/i2p/router/Router.java    | 316 +++++++++---------
 .../src/net/i2p/router/RouterVersion.java     |   4 +-
 .../i2p/router/transport/udp/PeerState.java   |  32 +-
 .../i2p/router/tunnel/pool/BuildHandler.java  |   1 +
 4 files changed, 184 insertions(+), 169 deletions(-)

diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 8dd3253e2d..e827540699 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -153,7 +153,7 @@ public class Router {
                 shutdown(EXIT_OOM); 
             }
         };
-        _shutdownHook = new ShutdownHook();
+        _shutdownHook = new ShutdownHook(_context);
         _gracefulShutdownDetector = new I2PThread(new GracefulShutdown());
         _gracefulShutdownDetector.setDaemon(true);
         _gracefulShutdownDetector.setName("Graceful shutdown hook");
@@ -210,7 +210,7 @@ public class Router {
     public void setRouterInfo(RouterInfo info) { 
         _routerInfo = info; 
         if (info != null)
-            _context.jobQueue().addJob(new PersistRouterInfoJob());
+            _context.jobQueue().addJob(new PersistRouterInfoJob(_context));
     }
 
     /**
@@ -245,8 +245,8 @@ public class Router {
         _context.tunnelDispatcher().startup();
         _context.inNetMessagePool().startup();
         startupQueue();
-        _context.jobQueue().addJob(new CoalesceStatsJob());
-        _context.jobQueue().addJob(new UpdateRoutingKeyModifierJob());
+        _context.jobQueue().addJob(new CoalesceStatsJob(_context));
+        _context.jobQueue().addJob(new UpdateRoutingKeyModifierJob(_context));
         warmupCrypto();
         _sessionKeyPersistenceHelper.startup();
         //_context.adminManager().startup();
@@ -449,89 +449,6 @@ public class Router {
         finalShutdown(EXIT_HARD_RESTART);
     }
     
-    /**
-     * coalesce the stats framework every minute
-     *
-     */
-    private final class CoalesceStatsJob extends JobImpl {
-        public CoalesceStatsJob() { 
-            super(Router.this._context); 
-            Router.this._context.statManager().createRateStat("bw.receiveBps", "How fast we receive data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
-            Router.this._context.statManager().createRateStat("bw.sendBps", "How fast we send data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
-            Router.this._context.statManager().createRateStat("router.activePeers", "How many peers we are actively talking with", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
-            Router.this._context.statManager().createRateStat("router.highCapacityPeers", "How many high capacity peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
-            Router.this._context.statManager().createRateStat("router.fastPeers", "How many fast peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
-        }
-        public String getName() { return "Coalesce stats"; }
-        public void runJob() {
-            Router.this._context.statManager().coalesceStats();
-            
-            RateStat receiveRate = _context.statManager().getRate("transport.receiveMessageSize");
-            if (receiveRate != null) {
-                Rate rate = receiveRate.getRate(60*1000);
-                if (rate != null) { 
-                    double bytes = rate.getLastTotalValue();
-                    double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d); 
-                    Router.this._context.statManager().addRateData("bw.receiveBps", (long)bps, 60*1000);
-                }
-            }
-
-            RateStat sendRate = _context.statManager().getRate("transport.sendMessageSize");
-            if (sendRate != null) {
-                Rate rate = sendRate.getRate(60*1000);
-                if (rate != null) {
-                    double bytes = rate.getLastTotalValue();
-                    double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d); 
-                    Router.this._context.statManager().addRateData("bw.sendBps", (long)bps, 60*1000);
-                }
-            }
-            
-            int active = Router.this._context.commSystem().countActivePeers();
-            Router.this._context.statManager().addRateData("router.activePeers", active, 60*1000);
-            
-            int fast = Router.this._context.profileOrganizer().countFastPeers();
-            Router.this._context.statManager().addRateData("router.fastPeers", fast, 60*1000);
-            
-            int highCap = Router.this._context.profileOrganizer().countHighCapacityPeers();
-            Router.this._context.statManager().addRateData("router.highCapacityPeers", highCap, 60*1000);
-
-            requeue(60*1000);
-        }
-    }
-    
-    /**
-     * Update the routing Key modifier every day at midnight (plus on startup).
-     * This is done here because we want to make sure the key is updated before anyone
-     * uses it.
-     */
-    private final class UpdateRoutingKeyModifierJob extends JobImpl {
-        private Calendar _cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
-        public UpdateRoutingKeyModifierJob() { super(Router.this._context); }
-        public String getName() { return "Update Routing Key Modifier"; }
-        public void runJob() {
-            Router.this._context.routingKeyGenerator().generateDateBasedModData();
-            requeue(getTimeTillMidnight());
-        }
-        private long getTimeTillMidnight() {
-            long now = Router.this._context.clock().now();
-            _cal.setTime(new Date(now));
-            _cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR));               // gcj <= 4.0 workaround
-            _cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
-            _cal.add(Calendar.DATE, 1);
-            _cal.set(Calendar.HOUR_OF_DAY, 0);
-            _cal.set(Calendar.MINUTE, 0);
-            _cal.set(Calendar.SECOND, 0);
-            _cal.set(Calendar.MILLISECOND, 0);
-            long then = _cal.getTime().getTime();
-            long howLong = then - now;
-            if (howLong < 0) // hi kaffe
-                howLong = 24*60*60*1000l + howLong;
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Time till midnight: " + howLong + "ms");
-            return howLong;
-        }
-    }
-    
     private void warmupCrypto() {
         _context.random().nextBoolean();
         new DHSessionKeyBuilder(); // load the class so it starts the precalc process
@@ -1060,7 +977,7 @@ public class Router {
         return _context.getProperty("router.pingFile", "router.ping");
     }
     
-    private static final long LIVELINESS_DELAY = 60*1000;
+    static final long LIVELINESS_DELAY = 60*1000;
     
     /** 
      * Start a thread that will periodically update the file "router.ping", but if 
@@ -1082,84 +999,177 @@ public class Router {
             }
         }
         // not an I2PThread for context creation issues
-        Thread t = new Thread(new MarkLiveliness(f));
+        Thread t = new Thread(new MarkLiveliness(_context, this, f));
         t.setName("Mark router liveliness");
         t.setDaemon(true);
         t.start();
         return true;
     }
-    
-    private class MarkLiveliness implements Runnable {
-        private File _pingFile;
-        public MarkLiveliness(File f) {
-            _pingFile = f;
-        }
-        public void run() {
-            _pingFile.deleteOnExit();
-            do {
-                ping();
-                try { Thread.sleep(LIVELINESS_DELAY); } catch (InterruptedException ie) {}
-            } while (_isAlive);
-            _pingFile.delete();
+}
+
+/**
+ * coalesce the stats framework every minute
+ *
+ */
+class CoalesceStatsJob extends JobImpl {
+    public CoalesceStatsJob(RouterContext ctx) { 
+        super(ctx); 
+        ctx.statManager().createRateStat("bw.receiveBps", "How fast we receive data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
+        ctx.statManager().createRateStat("bw.sendBps", "How fast we send data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
+        ctx.statManager().createRateStat("router.activePeers", "How many peers we are actively talking with", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
+        ctx.statManager().createRateStat("router.highCapacityPeers", "How many high capacity peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
+        ctx.statManager().createRateStat("router.fastPeers", "How many fast peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
+    }
+    public String getName() { return "Coalesce stats"; }
+    public void runJob() {
+        getContext().statManager().coalesceStats();
+
+        RateStat receiveRate = getContext().statManager().getRate("transport.receiveMessageSize");
+        if (receiveRate != null) {
+            Rate rate = receiveRate.getRate(60*1000);
+            if (rate != null) { 
+                double bytes = rate.getLastTotalValue();
+                double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d); 
+                getContext().statManager().addRateData("bw.receiveBps", (long)bps, 60*1000);
+            }
         }
-        
-        private void ping() {
-            FileOutputStream fos = null;
-            try { 
-                fos = new FileOutputStream(_pingFile);
-                fos.write(("" + System.currentTimeMillis()).getBytes());
-            } catch (IOException ioe) {
-                if (_log != null) {
-                    _log.log(Log.CRIT, "Error writing to ping file", ioe);
-                } else {
-                    System.err.println("Error writing to ping file");
-                    ioe.printStackTrace();
-                }
-            } finally {
-                if (fos != null) try { fos.close(); } catch (IOException ioe) {}
+
+        RateStat sendRate = getContext().statManager().getRate("transport.sendMessageSize");
+        if (sendRate != null) {
+            Rate rate = sendRate.getRate(60*1000);
+            if (rate != null) {
+                double bytes = rate.getLastTotalValue();
+                double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d); 
+                getContext().statManager().addRateData("bw.sendBps", (long)bps, 60*1000);
             }
         }
+
+        int active = getContext().commSystem().countActivePeers();
+        getContext().statManager().addRateData("router.activePeers", active, 60*1000);
+
+        int fast = getContext().profileOrganizer().countFastPeers();
+        getContext().statManager().addRateData("router.fastPeers", fast, 60*1000);
+
+        int highCap = getContext().profileOrganizer().countHighCapacityPeers();
+        getContext().statManager().addRateData("router.highCapacityPeers", highCap, 60*1000);
+
+        requeue(60*1000);
     }
-    
-    
-    private static int __id = 0;
-    private class ShutdownHook extends Thread {
-        private int _id;
-        public ShutdownHook() {
-            _id = ++__id;
-        }
-        public void run() {
-            setName("Router " + _id + " shutdown");
-            _log.log(Log.CRIT, "Shutting down the router...");
-            shutdown(EXIT_HARD);
+}
+
+/**
+ * Update the routing Key modifier every day at midnight (plus on startup).
+ * This is done here because we want to make sure the key is updated before anyone
+ * uses it.
+ */
+class UpdateRoutingKeyModifierJob extends JobImpl {
+    private Log _log;
+    private Calendar _cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+    public UpdateRoutingKeyModifierJob(RouterContext ctx) { 
+        super(ctx);
+    }
+    public String getName() { return "Update Routing Key Modifier"; }
+    public void runJob() {
+        _log = getContext().logManager().getLog(getClass());
+        getContext().routingKeyGenerator().generateDateBasedModData();
+        requeue(getTimeTillMidnight());
+    }
+    private long getTimeTillMidnight() {
+        long now = getContext().clock().now();
+        _cal.setTime(new Date(now));
+        _cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR));               // gcj <= 4.0 workaround
+        _cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
+        _cal.add(Calendar.DATE, 1);
+        _cal.set(Calendar.HOUR_OF_DAY, 0);
+        _cal.set(Calendar.MINUTE, 0);
+        _cal.set(Calendar.SECOND, 0);
+        _cal.set(Calendar.MILLISECOND, 0);
+        long then = _cal.getTime().getTime();
+        long howLong = then - now;
+        if (howLong < 0) // hi kaffe
+            howLong = 24*60*60*1000l + howLong;
+        if (_log.shouldLog(Log.DEBUG))
+            _log.debug("Time till midnight: " + howLong + "ms");
+        return howLong;
+    }
+}
+
+class MarkLiveliness implements Runnable {
+    private RouterContext _context;
+    private Router _router;
+    private File _pingFile;
+    public MarkLiveliness(RouterContext ctx, Router router, File pingFile) {
+        _context = ctx;
+        _router = router;
+        _pingFile = pingFile;
+    }
+    public void run() {
+        _pingFile.deleteOnExit();
+        do {
+            ping();
+            try { Thread.sleep(Router.LIVELINESS_DELAY); } catch (InterruptedException ie) {}
+        } while (_router.isAlive());
+        _pingFile.delete();
+    }
+
+    private void ping() {
+        FileOutputStream fos = null;
+        try { 
+            fos = new FileOutputStream(_pingFile);
+            fos.write(("" + System.currentTimeMillis()).getBytes());
+        } catch (IOException ioe) {
+            System.err.println("Error writing to ping file");
+            ioe.printStackTrace();
+        } finally {
+            if (fos != null) try { fos.close(); } catch (IOException ioe) {}
         }
     }
-    
-    /** update the router.info file whenever its, er, updated */
-    private class PersistRouterInfoJob extends JobImpl {
-        public PersistRouterInfoJob() { super(Router.this._context); }
-        public String getName() { return "Persist Updated Router Information"; }
-        public void runJob() {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Persisting updated router info");
+}
 
-            String infoFilename = getConfigSetting(PROP_INFO_FILENAME);
-            if (infoFilename == null)
-                infoFilename = PROP_INFO_FILENAME_DEFAULT;
+class ShutdownHook extends Thread {
+    private RouterContext _context;
+    private static int __id = 0;
+    private int _id;
+    public ShutdownHook(RouterContext ctx) {
+        _context = ctx;
+        _id = ++__id;
+    }
+    public void run() {
+        setName("Router " + _id + " shutdown");
+        Log l = _context.logManager().getLog(Router.class);
+        l.log(Log.CRIT, "Shutting down the router...");
+        _context.router().shutdown(Router.EXIT_HARD);
+    }
+}
 
-            RouterInfo info = getRouterInfo();
+/** update the router.info file whenever its, er, updated */
+class PersistRouterInfoJob extends JobImpl {
+    private Log _log;
+    public PersistRouterInfoJob(RouterContext ctx) { 
+        super(ctx); 
+    }
+    public String getName() { return "Persist Updated Router Information"; }
+    public void runJob() {
+        _log = getContext().logManager().getLog(PersistRouterInfoJob.class);
+        if (_log.shouldLog(Log.DEBUG))
+            _log.debug("Persisting updated router info");
 
-            FileOutputStream fos = null;
-            try {
-                fos = new FileOutputStream(infoFilename);
-                info.writeBytes(fos);
-            } catch (DataFormatException dfe) {
-                _log.error("Error rebuilding the router information", dfe);
-            } catch (IOException ioe) {
-                _log.error("Error writing out the rebuilt router information", ioe);
-            } finally {
-                if (fos != null) try { fos.close(); } catch (IOException ioe) {}
-            }
+        String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME);
+        if (infoFilename == null)
+            infoFilename = Router.PROP_INFO_FILENAME_DEFAULT;
+
+        RouterInfo info = getContext().router().getRouterInfo();
+
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(infoFilename);
+            info.writeBytes(fos);
+        } catch (DataFormatException dfe) {
+            _log.error("Error rebuilding the router information", dfe);
+        } catch (IOException ioe) {
+            _log.error("Error writing out the rebuilt router information", ioe);
+        } finally {
+            if (fos != null) try { fos.close(); } catch (IOException ioe) {}
         }
     }
-}
+}
\ No newline at end of file
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 993ecc2789..233e3e7c74 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
  *
  */
 public class RouterVersion {
-    public final static String ID = "$Revision: 1.356 $ $Date: 2006/02/21 10:20:20 $";
+    public final static String ID = "$Revision: 1.357 $ $Date: 2006/02/22 09:54:23 $";
     public final static String VERSION = "0.6.1.11";
-    public final static long BUILD = 1;
+    public final static long BUILD = 2;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java
index fb621aa8b7..cea6f8097c 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -835,13 +835,7 @@ public class PeerState {
         _messagesSent++;
         if (numSends < 2) {
             recalculateTimeouts(lifetime);
-            if (_mtu <= MIN_MTU) {
-                if (_context.random().nextInt(50*(int)_mtuDecreases) <= 0) {
-                    _context.statManager().addRateData("udp.mtuIncrease", _packetsRetransmitted, _packetsTransmitted);
-                    _mtu = LARGE_MTU;
-                    _mtuIncreases++;
-                }
-            }
+            adjustMTU();
         }
         else if (_log.shouldLog(Log.WARN))
             _log.warn("acked after numSends=" + numSends + " w/ lifetime=" + lifetime + " and size=" + bytesACKed);
@@ -870,14 +864,24 @@ public class PeerState {
             _rto = MAX_RTO;
     }
     
-    private void reduceMTU() {
-        if (_mtu > MIN_MTU) {
-            double retransPct = (double)_packetsRetransmitted/(double)_packetsTransmitted;
-            if (retransPct >= 0.05) { // should we go for lower?
-                _context.statManager().addRateData("udp.mtuDecrease", _packetsRetransmitted, _packetsTransmitted);
+    private void adjustMTU() {
+        double retransPct = 0;
+        if (_packetsTransmitted > 0) {
+            retransPct = (double)_packetsRetransmitted/(double)_packetsTransmitted;
+            boolean wantLarge = retransPct < .25d; // heuristic to allow fairly lossy links to use large MTUs
+            if (wantLarge && _mtu != LARGE_MTU) {
+                if (_context.random().nextLong(_mtuDecreases) <= 0) {
+                    _mtu = LARGE_MTU;
+                    _mtuIncreases++;
+                    _context.statManager().addRateData("udp.mtuIncrease", _mtuIncreases, _mtuDecreases);
+		}
+	    } else if (!wantLarge && _mtu == LARGE_MTU) {
                 _mtu = MIN_MTU;
                 _mtuDecreases++;
-            }
+                _context.statManager().addRateData("udp.mtuDecrease", _mtuDecreases, _mtuIncreases);
+	    }
+        } else {
+            _mtu = DEFAULT_MTU;
         }
     }
     
@@ -895,7 +899,7 @@ public class PeerState {
         }
         congestionOccurred();
         _context.statManager().addRateData("udp.congestedRTO", _rto, _rttDeviation);
-        reduceMTU();
+        adjustMTU();
         //_rto *= 2; 
     }
     public void packetsTransmitted(int packets) { 
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
index 282aa2285c..9e812296b3 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
@@ -188,6 +188,7 @@ class BuildHandler {
                 
                 if (howBad == 0) {
                     // w3wt
+                    _context.profileManager().tunnelJoined(peer, rtt);
                 } else {
                     allAgree = false;
                     switch (howBad) {
-- 
GitLab