mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Don't allow LifecycleManager to start and stop concurrently. Bug #68.
This commit is contained in:
@@ -4,6 +4,8 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -24,6 +26,7 @@ import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageAddedEvent;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager.StartResult;
|
||||
import org.briarproject.api.messaging.GroupId;
|
||||
|
||||
import roboguice.service.RoboService;
|
||||
@@ -87,11 +90,16 @@ public class BriarService extends RoboService implements EventListener {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(lifecycleManager.startServices()) {
|
||||
StartResult result = lifecycleManager.startServices();
|
||||
if(result == SUCCESS) {
|
||||
db.addListener(BriarService.this);
|
||||
started = true;
|
||||
} else if(result == ALREADY_RUNNING) {
|
||||
LOG.info("Already running");
|
||||
stopSelf();
|
||||
} else {
|
||||
LOG.info("Startup failed");
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.warning("Startup failed: " + result);
|
||||
showStartupFailureNotification();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,17 @@ package org.briarproject.api.lifecycle;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* Manages the lifecycle of the app, starting and stopping {@link Service
|
||||
* Services}, shutting down {@link java.util.concurrent.ExecutorService
|
||||
* ExecutorServices}, and opening and closing the {@link
|
||||
* org.briarproject.api.db.DatabaseComponent DatabaseComponent}.
|
||||
*/
|
||||
public interface LifecycleManager {
|
||||
|
||||
/** The result of calling {@link LifecycleManager#startServices()}. */
|
||||
enum StartResult { ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS }
|
||||
|
||||
/** Registers a {@link Service} to be started and stopped. */
|
||||
public void register(Service s);
|
||||
|
||||
@@ -14,27 +23,37 @@ public interface LifecycleManager {
|
||||
public void registerForShutdown(ExecutorService e);
|
||||
|
||||
/**
|
||||
* Starts any registered {@link Service}s and returns true if all services
|
||||
* started successfully.
|
||||
* Starts any registered {@link Service Services} and opens the {@link
|
||||
* org.briarproject.api.db.DatabaseComponent DatabaseComponent}.
|
||||
*/
|
||||
public boolean startServices();
|
||||
public StartResult startServices();
|
||||
|
||||
/**
|
||||
* Stops any registered {@link Service}s and shuts down any registered
|
||||
* {@link java.util.concurrent.ExecutorService ExecutorService}s.
|
||||
* Stops any registered {@link Service Services}, shuts down any
|
||||
* registered {@link java.util.concurrent.ExecutorService ExecutorServices},
|
||||
* and closes the {@link org.briarproject.api.db.DatabaseComponent
|
||||
* DatabaseComponent}.
|
||||
*/
|
||||
public void stopServices();
|
||||
|
||||
/** Waits for the database to be opened before returning. */
|
||||
/**
|
||||
* Waits for the {@link org.briarproject.api.db.DatabaseComponent
|
||||
* DatabaseComponent} to be opened before returning.
|
||||
*/
|
||||
public void waitForDatabase() throws InterruptedException;
|
||||
|
||||
/** Waits for all registered {@link Service}s to start before returning. */
|
||||
/**
|
||||
* Waits for the {@link org.briarproject.api.db.DatabaseComponent
|
||||
* DatabaseComponent} to be opened and all registered {@link Service
|
||||
* Services} to start before returning.
|
||||
*/
|
||||
public void waitForStartup() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Waits for all registered {@link Service}s to stop and all registered
|
||||
* {@link java.util.concurrent.ExecutorService ExecutorService}s to shut
|
||||
* down before returning.
|
||||
* Waits for all registered {@link Service Services} to stop, all
|
||||
* registered {@link java.util.concurrent.ExecutorService ExecutorServices}
|
||||
* to shut down, and the {@link org.briarproject.api.db.DatabaseComponent
|
||||
* DatabaseComponent} to be closed before returning.
|
||||
*/
|
||||
public void waitForShutdown() throws InterruptedException;
|
||||
}
|
||||
@@ -2,12 +2,17 @@ package org.briarproject.lifecycle;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.DB_ERROR;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR;
|
||||
import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -27,6 +32,7 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
private final DatabaseComponent db;
|
||||
private final Collection<Service> services;
|
||||
private final Collection<ExecutorService> executors;
|
||||
private final Semaphore startStopSemaphore = new Semaphore(1);
|
||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch startupLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
|
||||
@@ -51,12 +57,16 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
executors.add(e);
|
||||
}
|
||||
|
||||
public boolean startServices() {
|
||||
public StartResult startServices() {
|
||||
if(!startStopSemaphore.tryAcquire()) {
|
||||
LOG.info("Already starting or stopping");
|
||||
return ALREADY_RUNNING;
|
||||
}
|
||||
try {
|
||||
LOG.info("Starting");
|
||||
long start = clock.currentTimeMillis();
|
||||
LOG.info("Starting services");
|
||||
long now = clock.currentTimeMillis();
|
||||
boolean reopened = db.open();
|
||||
long duration = clock.currentTimeMillis() - start;
|
||||
long duration = clock.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO)) {
|
||||
if(reopened)
|
||||
LOG.info("Reopening database took " + duration + " ms");
|
||||
@@ -64,15 +74,15 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
}
|
||||
dbLatch.countDown();
|
||||
for(Service s : services) {
|
||||
start = clock.currentTimeMillis();
|
||||
now = clock.currentTimeMillis();
|
||||
boolean started = s.start();
|
||||
duration = clock.currentTimeMillis() - start;
|
||||
duration = clock.currentTimeMillis() - now;
|
||||
if(!started) {
|
||||
if(LOG.isLoggable(WARNING)) {
|
||||
String name = s.getClass().getName();
|
||||
LOG.warning(name + " did not start");
|
||||
}
|
||||
return false;
|
||||
return SERVICE_ERROR;
|
||||
}
|
||||
if(LOG.isLoggable(INFO)) {
|
||||
String name = s.getClass().getName();
|
||||
@@ -80,19 +90,27 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
}
|
||||
}
|
||||
startupLatch.countDown();
|
||||
return true;
|
||||
return SUCCESS;
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return false;
|
||||
return DB_ERROR;
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return false;
|
||||
return DB_ERROR;
|
||||
} finally {
|
||||
startStopSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopServices() {
|
||||
try {
|
||||
LOG.info("Shutting down");
|
||||
startStopSemaphore.acquire();
|
||||
} catch(InterruptedException e) {
|
||||
LOG.warning("Interrupted while waiting to stop services");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LOG.info("Stopping services");
|
||||
for(Service s : services) {
|
||||
boolean stopped = s.stop();
|
||||
if(LOG.isLoggable(INFO)) {
|
||||
@@ -111,6 +129,8 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
startStopSemaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class TestLifecycleModule extends AbstractModule {
|
||||
|
||||
public void registerForShutdown(ExecutorService e) {}
|
||||
|
||||
public boolean startServices() { return true; }
|
||||
public StartResult startServices() { return StartResult.SUCCESS; }
|
||||
|
||||
public void stopServices() {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user