Skip to content
Snippets Groups Projects
Commit 80ac368c authored by akwizgran's avatar akwizgran
Browse files

Start and stop plugins in parallel for faster startup and shutdown.

parent ee641db6
No related branches found
No related tags found
No related merge requests found
...@@ -7,11 +7,12 @@ import java.io.IOException; ...@@ -7,11 +7,12 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; 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.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
...@@ -54,8 +55,8 @@ class PluginManagerImpl implements PluginManager { ...@@ -54,8 +55,8 @@ class PluginManagerImpl implements PluginManager {
private final Poller poller; private final Poller poller;
private final ConnectionDispatcher dispatcher; private final ConnectionDispatcher dispatcher;
private final UiCallback uiCallback; private final UiCallback uiCallback;
private final List<SimplexPlugin> simplexPlugins; // Locking: this private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins; // Locking: this private final List<DuplexPlugin> duplexPlugins;
@Inject @Inject
PluginManagerImpl(@PluginExecutor ExecutorService pluginExecutor, PluginManagerImpl(@PluginExecutor ExecutorService pluginExecutor,
...@@ -72,83 +73,36 @@ class PluginManagerImpl implements PluginManager { ...@@ -72,83 +73,36 @@ class PluginManagerImpl implements PluginManager {
this.poller = poller; this.poller = poller;
this.dispatcher = dispatcher; this.dispatcher = dispatcher;
this.uiCallback = uiCallback; this.uiCallback = uiCallback;
simplexPlugins = new ArrayList<SimplexPlugin>(); simplexPlugins = new CopyOnWriteArrayList<SimplexPlugin>();
duplexPlugins = new ArrayList<DuplexPlugin>(); duplexPlugins = new CopyOnWriteArrayList<DuplexPlugin>();
} }
public synchronized int start() { public synchronized int start() {
Set<TransportId> ids = new HashSet<TransportId>();
// Instantiate and start the simplex plugins // Instantiate and start the simplex plugins
if(LOG.isLoggable(INFO)) LOG.info("Starting simplex plugins"); if(LOG.isLoggable(INFO)) LOG.info("Starting simplex plugins");
for(SimplexPluginFactory factory : simplexPluginConfig.getFactories()) { Collection<SimplexPluginFactory> sFactories =
TransportId id = factory.getId(); simplexPluginConfig.getFactories();
if(!ids.add(id)) { final CountDownLatch sLatch = new CountDownLatch(sFactories.size());
if(LOG.isLoggable(WARNING)) for(SimplexPluginFactory factory : sFactories) {
LOG.warning("Duplicate transport ID: " + id); pluginExecutor.execute(new SimplexPluginStarter(factory, sLatch));
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);
}
} }
// Instantiate and start the duplex plugins // Instantiate and start the duplex plugins
if(LOG.isLoggable(INFO)) LOG.info("Starting duplex plugins"); if(LOG.isLoggable(INFO)) LOG.info("Starting duplex plugins");
for(DuplexPluginFactory factory : duplexPluginConfig.getFactories()) { Collection<DuplexPluginFactory> dFactories =
TransportId id = factory.getId(); duplexPluginConfig.getFactories();
if(!ids.add(id)) { final CountDownLatch dLatch = new CountDownLatch(dFactories.size());
if(LOG.isLoggable(WARNING)) for(DuplexPluginFactory factory : dFactories) {
LOG.warning("Duplicate transport ID: " + id); pluginExecutor.execute(new DuplexPluginStarter(factory, dLatch));
continue; }
} // Wait for the plugins to start
DuplexCallback callback = new DuplexCallback(id); try {
DuplexPlugin plugin = factory.createPlugin(callback); sLatch.await();
if(plugin == null) { dLatch.await();
if(LOG.isLoggable(INFO)) { } catch(InterruptedException e) {
LOG.info(factory.getClass().getSimpleName() if(LOG.isLoggable(WARNING))
+ " did not create a plugin"); LOG.warning("Interrupted while starting plugins");
} Thread.currentThread().interrupt();
continue; return 0;
}
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);
}
} }
// Start the poller // Start the poller
if(LOG.isLoggable(INFO)) LOG.info("Starting poller"); if(LOG.isLoggable(INFO)) LOG.info("Starting poller");
...@@ -157,51 +111,175 @@ class PluginManagerImpl implements PluginManager { ...@@ -157,51 +111,175 @@ class PluginManagerImpl implements PluginManager {
plugins.addAll(duplexPlugins); plugins.addAll(duplexPlugins);
poller.start(Collections.unmodifiableList(plugins)); poller.start(Collections.unmodifiableList(plugins));
// Return the number of plugins successfully started // Return the number of plugins successfully started
return simplexPlugins.size() + duplexPlugins.size(); return plugins.size();
} }
public synchronized int stop() { public synchronized int stop() {
int stopped = 0;
// Stop the poller // Stop the poller
if(LOG.isLoggable(INFO)) LOG.info("Stopping poller"); if(LOG.isLoggable(INFO)) LOG.info("Stopping poller");
poller.stop(); poller.stop();
final AtomicInteger stopped = new AtomicInteger(0);
int plugins = simplexPlugins.size() + duplexPlugins.size();
final CountDownLatch latch = new CountDownLatch(plugins);
// Stop the simplex plugins // Stop the simplex plugins
if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins"); if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins");
for(SimplexPlugin plugin : simplexPlugins) { for(SimplexPlugin plugin : simplexPlugins) {
try { pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
plugin.stop();
stopped++;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
} }
simplexPlugins.clear();
// Stop the duplex plugins // Stop the duplex plugins
if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins"); if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins");
for(DuplexPlugin plugin : duplexPlugins) { for(DuplexPlugin plugin : duplexPlugins) {
try { pluginExecutor.execute(new PluginStopper(plugin, latch, stopped));
plugin.stop();
stopped++;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
} }
simplexPlugins.clear();
duplexPlugins.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 // Shut down the executors
if(LOG.isLoggable(INFO)) LOG.info("Stopping executors"); if(LOG.isLoggable(INFO)) LOG.info("Stopping executors");
pluginExecutor.shutdown(); pluginExecutor.shutdown();
androidExecutor.shutdown(); androidExecutor.shutdown();
// Return the number of plugins successfully stopped // 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>(); List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
for(DuplexPlugin d : duplexPlugins) for(DuplexPlugin d : duplexPlugins)
if(d.supportsInvitations()) supported.add(d); if(d.supportsInvitations()) supported.add(d);
return Collections.unmodifiableList(supported); 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 { private abstract class PluginCallbackImpl implements PluginCallback {
protected final TransportId id; protected final TransportId id;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment