diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index bd50242d..6263b0ab 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -353,6 +353,7 @@ public class Core { log.info("shutting down embedded router") router.shutdown(0) } + log.info("shutdown complete") } static main(args) { diff --git a/core/src/main/groovy/com/muwire/core/connection/Connection.groovy b/core/src/main/groovy/com/muwire/core/connection/Connection.groovy index b1e9cd30..9096d808 100644 --- a/core/src/main/groovy/com/muwire/core/connection/Connection.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/Connection.groovy @@ -1,7 +1,12 @@ package com.muwire.core.connection import java.util.concurrent.BlockingQueue +import java.util.concurrent.CountDownLatch +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadFactory +import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import java.util.logging.Level @@ -21,6 +26,17 @@ import net.i2p.data.Destination @Log abstract class Connection implements Closeable { + + /** + * This exists because the reset() method blocks for a long time + * even though the javadocs say it's non-blocking. When that gets + * fixed on the I2P side, this can be removed. + */ + private static final ExecutorService CLOSER = Executors.newCachedThreadPool( {r -> + def rv = new Thread(r,"connection closer") + rv.setDaemon(true) + rv + } as ThreadFactory) private static final int SEARCHES = 10 private static final long INTERVAL = 1000 @@ -82,7 +98,14 @@ abstract class Connection implements Closeable { log.info("closing $name") reader.interrupt() writer.interrupt() - endpoint.close() + + CountDownLatch latch = new CountDownLatch(1) + CLOSER.submit({ + endpoint.close() + latch.countDown() + log.info("closed $name") + }) + latch.await(3000, TimeUnit.MILLISECONDS) eventBus.publish(new DisconnectionEvent(destination: endpoint.destination)) } @@ -91,6 +114,7 @@ abstract class Connection implements Closeable { while(running.get()) { read() } + } catch (InterruptedException ok) { } catch (SocketTimeoutException e) { } catch (Exception e) { log.log(Level.WARNING,"unhandled exception in reader",e) @@ -107,6 +131,7 @@ abstract class Connection implements Closeable { def message = messages.take() write(message) } + } catch (InterruptedException ok) { } catch (Exception e) { log.log(Level.WARNING, "unhandled exception in writer",e) } finally { diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy index bac34eac..7d3a8a5b 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy @@ -36,9 +36,8 @@ abstract class ConnectionManager { timer.schedule({sendPings()} as TimerTask, 1000,1000) } - void stop() { + void shutdown() { timer.cancel() - getConnections().each { it.close() } } void onTrustEvent(TrustEvent e) { @@ -62,8 +61,6 @@ abstract class ConnectionManager { abstract void onDisconnectionEvent(DisconnectionEvent e) - abstract void shutdown() - protected void sendPings() { final long now = System.currentTimeMillis() getConnections().each { diff --git a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy index 719588d1..4aaa405b 100644 --- a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy @@ -104,6 +104,7 @@ class UltrapeerConnectionManager extends ConnectionManager { @Override void shutdown() { + super.shutdown() peerConnections.values().stream().parallel().forEach({v -> v.close()}) leafConnections.values().stream().parallel().forEach({v -> v.close()}) peerConnections.clear() diff --git a/gui/griffon-app/lifecycle/Shutdown.groovy b/gui/griffon-app/lifecycle/Shutdown.groovy index 06df819a..9ee2c9bd 100644 --- a/gui/griffon-app/lifecycle/Shutdown.groovy +++ b/gui/griffon-app/lifecycle/Shutdown.groovy @@ -18,11 +18,8 @@ class Shutdown extends AbstractLifecycleHandler { @Override void execute() { - log.info("shutting down") + log.info("shutting down from lifecycle") Core core = application.context.get("core") - if (core != null) { - Thread t = new Thread({ core.shutdown() } as Runnable) - t.start() - } + core?.shutdown() } }