org.jgroups.blocks.executor.ExecutionRunner Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.blocks.executor;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jgroups.JChannel;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.Executing;
/**
* This class is to be used to pick up execution requests and actually run
* them. A single instance can be used across any number of threads.
*
* @author wburns
*/
public class ExecutionRunner implements Runnable {
protected JChannel ch;
protected Executing _execProt;
public ExecutionRunner(JChannel channel) {
setChannel(channel);
}
public void setChannel(JChannel ch) {
this.ch=ch;
_execProt=(Executing)ch.getProtocolStack().findProtocol(Executing.class);
if(_execProt == null)
throw new IllegalStateException("Channel configuration must include a executing protocol " +
"(subclass of " + Executing.class.getName() + ")");
}
protected static class Holder {
protected T value;
public Holder(T value) {
this.value = value;
}
}
// @see java.lang.Runnable#run()
@Override
public void run() {
final Lock shutdownLock = new ReentrantLock();
// The following 2 atomic boolean should only ever be updated while
// protected by the above lock. They don't have to be atomic boolean,
// but it is a nice wrapper to share a reference between threads.
// Reads can be okay in certain circumstances
final AtomicBoolean canInterrupt = new AtomicBoolean(true);
final AtomicBoolean shutdown = new AtomicBoolean();
// This thread is only spawned so that we can differentiate between
// an interrupt of a task and an interrupt causing a shutdown of
// runner itself.
Thread executionThread = new Thread() {
// @see java.lang.Thread#run()
@Override
public void run() {
Thread currentThread = Thread.currentThread();
Runnable runnable = null;
// This task exits by being interrupted when the task isn't running
// or by updating shutdown to true when it can't be interrupted
while (!shutdown.get()) {
_runnables.put(currentThread, new Holder(
null));
runnable = (Runnable)ch.down(new ExecutorEvent(
ExecutorEvent.CONSUMER_READY, null));
// This means we were interrupted while waiting
if (runnable == null) {
break;
}
// First retrieve the lock to make sure we can tell them
// to not interrupt us
shutdownLock.lock();
try {
// Clear interrupt state as we don't want to stop the
// task we just received. If we got a shutdown signal
// we will only do it after we loop back around.
Thread.interrupted();
canInterrupt.set(false);
}
finally {
shutdownLock.unlock();
}
_runnables.put(currentThread, new Holder<>(
runnable));
Throwable throwable = null;
try {
runnable.run();
}
// This can only happen if user is directly doing an execute(Runnable)
catch (Throwable t) {
_logger.error("Unexpected Runtime Error encountered in Runnable request", t);
throwable = t;
}
ch.down(new ExecutorEvent(ExecutorEvent.TASK_COMPLETE,
throwable != null ? new Object[]{runnable, throwable} : runnable));
// We have to let the outer thread know we can now be interrupted
shutdownLock.lock();
try {
canInterrupt.set(true);
}
finally {
shutdownLock.unlock();
}
}
_runnables.remove(currentThread);
}
};
executionThread.setName(Thread.currentThread().getName() + "- Task Runner");
executionThread.start();
try {
executionThread.join();
}
catch (InterruptedException e) {
shutdownLock.lock();
try {
if (canInterrupt.get()) {
executionThread.interrupt();
}
shutdown.set(true);
}
finally {
shutdownLock.unlock();
}
if (_logger.isTraceEnabled()) {
_logger.trace("Shutting down Execution Runner");
}
}
}
/**
* Returns a copy of the runners being used with the runner and what threads.
* If a thread is not currently running a task it will return with a null
* value. This map is a copy and can be modified if necessary without
* causing issues.
* @return map of all threads that are active with this runner. If the
* thread is currently running a job the runnable value will be
* populated otherwise null would mean the thread is waiting
*/
public Map getCurrentRunningTasks() {
Map map = new HashMap<>();
for (Entry> entry : _runnables.entrySet()) {
map.put(entry.getKey(), entry.getValue().value);
}
return map;
}
private final Map> _runnables =
new ConcurrentHashMap<>();
protected static final Log _logger = LogFactory.getLog(ExecutionRunner.class);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy