diff --git a/apps/BOB/nbproject/private/private.xml b/apps/BOB/nbproject/private/private.xml index c1f155a782bd6f432a8846f3d3b308ba6fa6856c..7d9997b3a07c00e2cea501c4899c6ec091c32c10 100644 --- a/apps/BOB/nbproject/private/private.xml +++ b/apps/BOB/nbproject/private/private.xml @@ -1,4 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <project-private xmlns="http://www.netbeans.org/ns/project-private/1"> <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/> + <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1"> + <file>file:/root/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/BOB.java</file> + <file>file:/root/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/DoCMDS.java</file> + <file>file:/root/NetBeansProjects/i2p.i2p/apps/BOB/src/net/i2p/BOB/MUXlisten.java</file> + </open-files> </project-private> diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java index c49be9826221474eeed15ea320662c3858ed69ee..cc92e66cae1b1b3e49b5cca2a1409f3aebf370de 100644 --- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java +++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java @@ -36,7 +36,6 @@ import net.i2p.I2PException; import net.i2p.client.I2PClientFactory; import net.i2p.data.Destination; import net.i2p.util.Log; -import net.i2p.util.SimpleStore; /** * Simplistic command parser for BOB @@ -98,6 +97,7 @@ public class DoCMDS implements Runnable { private static final String C_status = "status"; private static final String C_stop = "stop"; private static final String C_verify = "verify"; + private static final String C_visit = "visit"; private static final String C_zap = "zap"; /* all the coomands available, plus description */ @@ -124,6 +124,7 @@ public class DoCMDS implements Runnable { {C_status, C_status + " nickname * Display status of a nicknamed tunnel."}, {C_stop, C_stop + " * Stops the current nicknamed tunnel."}, {C_verify, C_verify + " BASE64_key * Verifies BASE64 destination."}, + {C_visit, C_visit + " * Thread dump to wrapper.log."}, {C_zap, C_zap + " * Shuts down BOB."}, {"", "COMMANDS: " + // this is ugly, but... C_help + " " + @@ -148,13 +149,14 @@ public class DoCMDS implements Runnable { C_status + " " + C_stop + " " + C_verify + " " + + C_visit + " " + C_zap }, {" ", " "} // end of list }; /** - * @parm LIVE + * @param LIVE * @param server * @param props * @param database @@ -438,6 +440,9 @@ public class DoCMDS implements Runnable { } } + } else if (Command.equals(C_visit)) { + visitAllThreads(); + out.println("OK "); } else if (Command.equals(C_getdest)) { if (ns) { if (dk) { @@ -1274,7 +1279,7 @@ public class DoCMDS implements Runnable { } else { MUXlisten tunnel; try { - while(!lock.compareAndSet(false, true)) { + while (!lock.compareAndSet(false, true)) { // wait } tunnel = new MUXlisten(lock, database, nickinfo, _log); @@ -1445,4 +1450,48 @@ public class DoCMDS implements Runnable { ioe.printStackTrace(); } } + // Debugging... None of this is normally used. + + /** + * Find the root thread group and print them all. + * + */ + private void visitAllThreads() { + ThreadGroup root = Thread.currentThread().getThreadGroup().getParent(); + while (root.getParent() != null) { + root = root.getParent(); + } + + // Visit each thread group + visit(root, 0, root.getName()); + } + + /** + * Recursively visits all thread groups under `group' and dumps them. + * @param group ThreadGroup to visit + * @param level Current level + */ + private static void visit(ThreadGroup group, int level, String tn) { + // Get threads in `group' + int numThreads = group.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + numThreads = group.enumerate(threads, false); + String indent = "------------------------------------".substring(0, level) + "-> "; + // Enumerate each thread in `group' and print it. + for (int i = 0; i < numThreads; i++) { + // Get thread + Thread thread = threads[i]; + System.out.println("BOB: " + indent + tn + ": " +thread.toString()); + } + + // Get thread subgroups of `group' + int numGroups = group.activeGroupCount(); + ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; + numGroups = group.enumerate(groups, false); + + // Recursively visit each subgroup + for (int i = 0; i < numGroups; i++) { + visit(groups[i], level + 1, groups[i].getName()); + } + } } diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java index 73e936c61b3cf7b77970820a63dbcdd9c8eb01bf..91ac0558955687b85f5f472abcaf98e526e9545e 100644 --- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java +++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java @@ -74,6 +74,8 @@ public class I2PtoTCP implements Runnable { OutputStream out = null; InputStream Iin = null; OutputStream Iout = null; + Thread t = null; + Thread q = null; try { die: { @@ -113,17 +115,33 @@ public class I2PtoTCP implements Runnable { // setup to cross the streams TCPio conn_c = new TCPio(in, Iout /*, info, database */); // app -> I2P TCPio conn_a = new TCPio(Iin, out /* , info, database */); // I2P -> app - Thread t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); - Thread q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); + t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); + q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); // Fire! t.start(); q.start(); - while (t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread + boolean spin = true; + while (t.isAlive() && q.isAlive() && spin) { // AND is used here to kill off the other thread try { Thread.sleep(10); //sleep for 10 ms } catch (InterruptedException e) { break die; } + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + spin = info.get("RUNNING").equals(Boolean.TRUE); + } catch (Exception e) { + try { + runlock(); + } catch (Exception e2) { + break die; + } + break die; + } } // System.out.println("I2PtoTCP: Going away..."); } catch (Exception e) { @@ -132,6 +150,14 @@ public class I2PtoTCP implements Runnable { } } // die } finally { + try { + t.interrupt(); + } catch (Exception e) { + } + try { + q.interrupt(); + } catch (Exception e) { + } try { in.close(); } catch (Exception ex) { diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java index 5f6885dd5874b4c68e058f2afc78f90872bae031..2f22abbeb483a1663c1679832de97b02eb620135 100644 --- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java +++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java @@ -159,7 +159,6 @@ public class MUXlisten implements Runnable { { try { tg = new ThreadGroup(N); - die: { // toss the connections to a new threads. // will wrap with TCP and UDP when UDP works @@ -185,22 +184,22 @@ public class MUXlisten implements Runnable { info.add("STARTING", new Boolean(false)); } catch (Exception e) { wunlock(); - break die; + break quit; } } catch (Exception e) { - break die; + break quit; } try { wunlock(); } catch (Exception e) { - break die; + break quit; } boolean spin = true; while (spin) { try { Thread.sleep(1000); //sleep for 1 second } catch (InterruptedException e) { - break die; + break quit; } try { rlock(); @@ -208,35 +207,17 @@ public class MUXlisten implements Runnable { spin = info.get("STOPPING").equals(Boolean.FALSE); } catch (Exception e) { runlock(); - break die; + break quit; } } catch (Exception e) { - break die; + break quit; } try { runlock(); } catch (Exception e) { - break die; + break quit; } } - /* cleared in the finally... - try { - wlock(); - try { - info.add("RUNNING", new Boolean(false)); - } catch (Exception e) { - wunlock(); - break die; - } - } catch (Exception e) { - break die; - } - try { - wunlock(); - } catch (Exception e) { - break die; - } - */ } // die } catch (Exception e) { @@ -278,11 +259,6 @@ public class MUXlisten implements Runnable { } } - try { - socketManager.destroySocketManager(); - } catch (Exception e) { - // nop - } // Some grace time. try { Thread.sleep(250); @@ -293,25 +269,27 @@ public class MUXlisten implements Runnable { // Wait around till all threads are collected. if (tg != null) { String boner = tg.getName(); + System.out.println("BOB: MUXlisten: Starting thread collection for: " + boner); _log.warn("BOB: MUXlisten: Starting thread collection for: " + boner); // tg.interrupt(); // give my stuff a small smack again. if (tg.activeCount() + tg.activeGroupCount() != 0) { + visit(tg, 0, boner); int foo = tg.activeCount() + tg.activeGroupCount(); // hopefully no longer needed! - // int bar = foo; - // System.out.println("BOB: MUXlisten: Waiting on threads for " + boner); - // System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); - // visit(tg, 0, boner); - // System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n"); + int bar = foo; + System.out.println("BOB: MUXlisten: Waiting on threads for " + boner); + System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); + visit(tg, 0, boner); + System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n"); // Happily spin forever :-( while (foo != 0) { foo = tg.activeCount() + tg.activeGroupCount(); - // if (foo != bar) { - // System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); - // visit(tg, 0, boner); - // System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n"); - // } - // bar = foo; + if (foo != bar && foo != 0) { + System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner); + visit(tg, 0, boner); + System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n"); + } + bar = foo; try { Thread.sleep(100); //sleep for 100 ms (One tenth second) } catch (InterruptedException ex) { @@ -319,11 +297,18 @@ public class MUXlisten implements Runnable { } } } + System.out.println("BOB: MUXlisten: Threads went away. Success: " + boner); _log.warn("BOB: MUXlisten: Threads went away. Success: " + boner); tg.destroy(); // Zap reference to the ThreadGroup so the JVM can GC it. tg = null; } + try { + socketManager.destroySocketManager(); + } catch (Exception e) { + // nop + } + } } diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java index f3f2c74456ed3e3d1d6e4ccc7b07d99505ce0942..117c2b1036fd8cb033d36f3531c745bcf6708131 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java +++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java @@ -45,7 +45,7 @@ import net.i2p.i2ptunnel.I2PTunnel; public class TCPtoI2P implements Runnable { private I2PSocket I2P; - // private NamedDB info, database; + private NamedDB info, database; private Socket sock; private I2PSocketManager socketManager; @@ -108,6 +108,16 @@ public class TCPtoI2P implements Runnable { out.flush(); } + private void rlock() throws Exception { + database.getReadLock(); + info.getReadLock(); + } + + private void runlock() throws Exception { + database.releaseReadLock(); + info.releaseReadLock(); + } + /** * TCP stream to I2P stream thread starter * @@ -118,6 +128,8 @@ public class TCPtoI2P implements Runnable { OutputStream Iout = null; InputStream in = null; OutputStream out = null; + Thread t = null; + Thread q = null; try { try { @@ -145,15 +157,18 @@ public class TCPtoI2P implements Runnable { // setup to cross the streams TCPio conn_c = new TCPio(in, Iout /*, info, database */); // app -> I2P TCPio conn_a = new TCPio(Iin, out /*, info, database */); // I2P -> app - Thread t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); - Thread q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); + t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA"); + q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB"); // Fire! t.start(); q.start(); + boolean spin = true; while (t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread Thread.sleep(10); //sleep for 10 ms + rlock(); + spin = info.get("RUNNING").equals(Boolean.TRUE); + runlock(); } - } catch (I2PException e) { Emsg("ERROR " + e.toString(), out); } catch (ConnectException e) { @@ -171,6 +186,14 @@ public class TCPtoI2P implements Runnable { // bail on anything else } } finally { + try { + t.interrupt(); + } catch (Exception e) { + } + try { + q.interrupt(); + } catch (Exception e) { + } try { in.close(); } catch (Exception e) { diff --git a/history.txt b/history.txt index 6e62d88519b33424786c44823ecacd3a94e515d5..3191791194caf28bf10e6450284d6198b55bda6c 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,12 @@ +2009-06-05 sponge + * BOB now cleans up tunnels, although they can take up to 5 minutes to + disapear. This is due to the fact that the streaming lib doesn't + actually remove the connections properly and kill them off when the + manager is destroyed. I'm not certain if this is a bug, or a feature, + but it sure is annoying, and you have to wait for the connections to + time out. What should happen is the streaming lib should cause an IO + error to the pending read or write. + 2009-05-30 zzz * Console: - config.jsp now cause graceful restart diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 1440f81c074b5860a9c3b62a49546af04a8d5515..217d41d542754aad1a590ff923a6689b8d62edfa 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 10; + public final static long BUILD = 11; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;