From 80ac368cab046c443cc551972e3b0b5873256a96 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Thu, 25 Apr 2013 19:34:46 +0100
Subject: [PATCH] Start and stop plugins in parallel for faster startup and
 shutdown.

---
 .../sf/briar/plugins/PluginManagerImpl.java   | 258 ++++++++++++------
 1 file changed, 168 insertions(+), 90 deletions(-)

diff --git a/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java b/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
index fcc486c4fc..ced415caec 100644
--- a/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
+++ b/briar-core/src/net/sf/briar/plugins/PluginManagerImpl.java
@@ -7,11 +7,12 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Logger;
 
 import net.sf.briar.api.ContactId;
@@ -54,8 +55,8 @@ class PluginManagerImpl implements PluginManager {
 	private final Poller poller;
 	private final ConnectionDispatcher dispatcher;
 	private final UiCallback uiCallback;
-	private final List<SimplexPlugin> simplexPlugins; // Locking: this
-	private final List<DuplexPlugin> duplexPlugins; // Locking: this
+	private final List<SimplexPlugin> simplexPlugins;
+	private final List<DuplexPlugin> duplexPlugins;
 
 	@Inject
 	PluginManagerImpl(@PluginExecutor ExecutorService pluginExecutor,
@@ -72,83 +73,36 @@ class PluginManagerImpl implements PluginManager {
 		this.poller = poller;
 		this.dispatcher = dispatcher;
 		this.uiCallback = uiCallback;
-		simplexPlugins = new ArrayList<SimplexPlugin>();
-		duplexPlugins = new ArrayList<DuplexPlugin>();
+		simplexPlugins = new CopyOnWriteArrayList<SimplexPlugin>();
+		duplexPlugins = new CopyOnWriteArrayList<DuplexPlugin>();
 	}
 
 	public synchronized int start() {
-		Set<TransportId> ids = new HashSet<TransportId>();
 		// Instantiate and start the simplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Starting simplex plugins");
-		for(SimplexPluginFactory factory : simplexPluginConfig.getFactories()) {
-			TransportId id = factory.getId();
-			if(!ids.add(id)) {
-				if(LOG.isLoggable(WARNING))
-					LOG.warning("Duplicate transport ID: " + id);
-				continue;
-			}
-			SimplexCallback callback = new SimplexCallback(id);
-			SimplexPlugin plugin = factory.createPlugin(callback);
-			if(plugin == null) {
-				if(LOG.isLoggable(INFO)) {
-					LOG.info(factory.getClass().getSimpleName()
-							+ " did not create a plugin");
-				}
-				continue;
-			}
-			try {
-				db.addTransport(id, plugin.getMaxLatency());
-			} catch(DbException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				continue;
-			}
-			try {
-				if(plugin.start()) {
-					simplexPlugins.add(plugin);
-				} else {
-					if(LOG.isLoggable(INFO))
-						LOG.info(plugin.getClass().getSimpleName()
-								+ " did not start");
-				}
-			} catch(IOException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			}
+		Collection<SimplexPluginFactory> sFactories =
+				simplexPluginConfig.getFactories();
+		final CountDownLatch sLatch = new CountDownLatch(sFactories.size());
+		for(SimplexPluginFactory factory : sFactories) {
+			pluginExecutor.execute(new SimplexPluginStarter(factory, sLatch));
 		}
 		// Instantiate and start the duplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Starting duplex plugins");
-		for(DuplexPluginFactory factory : duplexPluginConfig.getFactories()) {
-			TransportId id = factory.getId();
-			if(!ids.add(id)) {
-				if(LOG.isLoggable(WARNING))
-					LOG.warning("Duplicate transport ID: " + id);
-				continue;
-			}
-			DuplexCallback callback = new DuplexCallback(id);
-			DuplexPlugin plugin = factory.createPlugin(callback);
-			if(plugin == null) {
-				if(LOG.isLoggable(INFO)) {
-					LOG.info(factory.getClass().getSimpleName()
-							+ " did not create a plugin");
-				}
-				continue;
-			}
-			try {
-				db.addTransport(id, plugin.getMaxLatency());
-			} catch(DbException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				continue;
-			}
-			try {
-				if(plugin.start()) {
-					duplexPlugins.add(plugin);
-				} else {
-					if(LOG.isLoggable(INFO))
-						LOG.info(plugin.getClass().getSimpleName()
-								+ " did not start");
-				}
-			} catch(IOException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			}
+		Collection<DuplexPluginFactory> dFactories =
+				duplexPluginConfig.getFactories();
+		final CountDownLatch dLatch = new CountDownLatch(dFactories.size());
+		for(DuplexPluginFactory factory : dFactories) {
+			pluginExecutor.execute(new DuplexPluginStarter(factory, dLatch));
+		}
+		// Wait for the plugins to start
+		try {
+			sLatch.await();
+			dLatch.await();
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(WARNING))
+				LOG.warning("Interrupted while starting plugins");
+			Thread.currentThread().interrupt();
+			return 0;
 		}
 		// Start the poller
 		if(LOG.isLoggable(INFO)) LOG.info("Starting poller");
@@ -157,51 +111,175 @@ class PluginManagerImpl implements PluginManager {
 		plugins.addAll(duplexPlugins);
 		poller.start(Collections.unmodifiableList(plugins));
 		// Return the number of plugins successfully started
-		return simplexPlugins.size() + duplexPlugins.size();
+		return plugins.size();
 	}
 
 	public synchronized int stop() {
-		int stopped = 0;
 		// Stop the poller
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping poller");
 		poller.stop();
+		final AtomicInteger stopped = new AtomicInteger(0);
+		int plugins = simplexPlugins.size() + duplexPlugins.size();
+		final CountDownLatch latch = new CountDownLatch(plugins);
 		// Stop the simplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins");
 		for(SimplexPlugin plugin : simplexPlugins) {
-			try {
-				plugin.stop();
-				stopped++;
-			} catch(IOException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			}
+			pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
 		}
-		simplexPlugins.clear();
 		// Stop the duplex plugins
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins");
 		for(DuplexPlugin plugin : duplexPlugins) {
-			try {
-				plugin.stop();
-				stopped++;
-			} catch(IOException e) {
-				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			}
+			pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
 		}
+		simplexPlugins.clear();
 		duplexPlugins.clear();
+		// Wait for all the plugins to stop
+		try {
+			latch.await();
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(WARNING))
+				LOG.warning("Interrupted while stopping plugins");
+			Thread.currentThread().interrupt();
+			return 0;
+		}
 		// Shut down the executors
 		if(LOG.isLoggable(INFO)) LOG.info("Stopping executors");
 		pluginExecutor.shutdown();
 		androidExecutor.shutdown();
 		// Return the number of plugins successfully stopped
-		return stopped;
+		return stopped.get();
 	}
 
-	public synchronized Collection<DuplexPlugin> getInvitationPlugins() {
+	public Collection<DuplexPlugin> getInvitationPlugins() {
 		List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
 		for(DuplexPlugin d : duplexPlugins)
 			if(d.supportsInvitations()) supported.add(d);
 		return Collections.unmodifiableList(supported);
 	}
 
+	private class SimplexPluginStarter implements Runnable {
+
+		private final SimplexPluginFactory factory;
+		private final CountDownLatch latch;
+
+		private SimplexPluginStarter(SimplexPluginFactory factory,
+				CountDownLatch latch) {
+			this.factory = factory;
+			this.latch = latch;
+		}
+
+		public void run() {
+			try {
+				TransportId id = factory.getId();
+				SimplexCallback callback = new SimplexCallback(id);
+				SimplexPlugin plugin = factory.createPlugin(callback);
+				if(plugin == null) {
+					if(LOG.isLoggable(INFO)) {
+						String name = factory.getClass().getSimpleName();
+						LOG.info(name + " did not create a plugin");
+					}
+					return;
+				}
+				try {
+					db.addTransport(id, plugin.getMaxLatency());
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					return;
+				}
+				try {
+					if(plugin.start()) {
+						simplexPlugins.add(plugin);
+					} else {
+						if(LOG.isLoggable(INFO)) {
+							String name = plugin.getClass().getSimpleName();
+							LOG.info(name + " did not start");
+						}
+					}
+				} catch(IOException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			} finally {
+				latch.countDown();
+			}
+		}
+	}
+
+	private class DuplexPluginStarter implements Runnable {
+
+		private final DuplexPluginFactory factory;
+		private final CountDownLatch latch;
+
+		private DuplexPluginStarter(DuplexPluginFactory factory,
+				CountDownLatch latch) {
+			this.factory = factory;
+			this.latch = latch;
+		}
+
+		public void run() {
+			try {
+				TransportId id = factory.getId();
+				DuplexCallback callback = new DuplexCallback(id);
+				DuplexPlugin plugin = factory.createPlugin(callback);
+				if(plugin == null) {
+					if(LOG.isLoggable(INFO)) {
+						String name = factory.getClass().getSimpleName();
+						LOG.info(name + " did not create a plugin");
+					}
+					return;
+				}
+				try {
+					db.addTransport(id, plugin.getMaxLatency());
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					return;
+				}
+				try {
+					if(plugin.start()) {
+						duplexPlugins.add(plugin);
+					} else {
+						if(LOG.isLoggable(INFO)) {
+							String name = plugin.getClass().getSimpleName();
+							LOG.info(name + " did not start");
+						}
+					}
+				} catch(IOException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			} finally {
+				latch.countDown();
+			}
+		}
+	}
+
+	private class PluginStopper implements Runnable {
+
+		private final Plugin plugin;
+		private final CountDownLatch latch;
+		private final AtomicInteger stopped;
+
+		private PluginStopper(Plugin plugin, CountDownLatch latch,
+				AtomicInteger stopped) {
+			this.plugin = plugin;
+			this.latch = latch;
+			this.stopped = stopped;
+		}
+
+		public void run() {
+			try {
+				plugin.stop();
+				stopped.incrementAndGet();
+			} catch(IOException e) {
+				if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			} finally {
+				latch.countDown();
+			}
+		}
+	}
+
 	private abstract class PluginCallbackImpl implements PluginCallback {
 
 		protected final TransportId id;
-- 
GitLab