jalse.actions.AbstractActionEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of JALSE Show documentation
Show all versions of JALSE Show documentation
Java Artificial Life Simulation Engine
package jalse.actions;
import static jalse.actions.Actions.requireNotShutdown;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An abstract implementation of {@link ActionEngine} that is designed to be used with
* {@link ExecutorService}. This is a convenience class for creating an {@link ActionEngine}.
*
* If the engine is paused the incoming resumed state change can be awaited (
* {@link #awaitResumed()}).
*
* @author Elliot Ford
*
* @see #TERMINATION_TIMEOUT
* @see DefaultActionBindings
*/
public abstract class AbstractActionEngine implements ActionEngine {
/**
* How long the engine will wait until it times out and interrupts running threads on shutdown
* (configured via {@code jalse.actions.termination_timeout} system property).
*/
public static long TERMINATION_TIMEOUT = Long.valueOf(System.getProperty("jalse.actions.termination_timeout",
"2000"));
private static final Logger logger = Logger.getLogger(AbstractActionEngine.class.getName());
/**
* Executor service to be used for action scheduling.
*/
protected final ExecutorService executorService;
private final MutableActionBindings bindings;
private final Lock lock;
private final Condition resumed;
private final AtomicBoolean paused;
/**
* Creates a new instance of AbstractActionEngine with the supplied executor service.
*
* @param executorService
* Service to use.
*
* @see Actions#requireNotShutdown(ExecutorService)
*/
protected AbstractActionEngine(final ExecutorService executorService) {
this.executorService = requireNotShutdown(executorService);
bindings = new DefaultActionBindings();
lock = new ReentrantLock();
resumed = lock.newCondition();
paused = new AtomicBoolean();
}
/**
* Will wait until the engine has resumed (or stopped).
*
* @throws InterruptedException
* If the current thread was interrupted.
*/
protected void awaitResumed() throws InterruptedException {
lock.lockInterruptibly();
try {
while (isPaused()) {
resumed.await();
}
} finally {
lock.unlock();
}
}
@Override
public MutableActionBindings getBindings() {
return bindings;
}
@Override
public boolean isPaused() {
return paused.get() && !isStopped();
}
@Override
public boolean isStopped() {
return executorService.isShutdown();
}
@Override
public void pause() {
requireNotShutdown(executorService);
if (!paused.getAndSet(true)) {
logger.info("Engine paused");
}
}
@Override
public void resume() {
requireNotShutdown(executorService);
if (paused.getAndSet(false)) {
lock.lock();
try {
resumed.signalAll(); // Wake up and work!
} finally {
lock.unlock();
}
logger.info("Engine resumed");
}
}
@Override
public void stop() {
requireNotShutdown(executorService);
executorService.shutdown();
try {
if (!executorService.awaitTermination(TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow(); // Uh-oh
}
} catch (final InterruptedException e) {
logger.log(Level.WARNING, "Error terminating executor", e);
Thread.currentThread().interrupt();
}
logger.info("Engine shutdown");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy