xapi.process.impl.ConcurrencyServiceAbstract Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-dev Show documentation
Show all versions of xapi-dev Show documentation
Everything needed to run a comprehensive dev environment.
Just type X_ and pick a service from autocomplete;
new dev modules will be added as they are built.
The only dev service not included in the uber jar is xapi-dev-maven,
as it includes all runtime dependencies of maven, adding ~4 seconds to build time,
and 6 megabytes to the final output jar size (without xapi-dev-maven, it's ~1MB).
The newest version!
package xapi.process.impl;
import static xapi.util.X_Debug.debug;
import java.lang.Thread.State;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Provider;
import xapi.collect.impl.AbstractMultiInitMap;
import xapi.log.X_Log;
import xapi.process.api.ConcurrentEnvironment;
import xapi.process.api.ConcurrentEnvironment.Priority;
import xapi.process.api.Process;
import xapi.process.api.ProcessController;
import xapi.process.service.ConcurrencyService;
import xapi.util.X_Runtime;
import xapi.util.X_Util;
import xapi.util.api.ConvertsValue;
import xapi.util.api.ReceivesValue;
public abstract class ConcurrencyServiceAbstract implements ConcurrencyService{
protected class WrappedRunnable implements Runnable {
private Runnable core;
public WrappedRunnable(Runnable core) {
this.core = core;
}
@Override
public void run() {
core.run();
destroy(Thread.currentThread(), threadFlushTime());
//Now that we've finished the job we were told to do,
//let's attempt to reuse our current thread
//We should choose how to "steal work" wisely,
//using an algorithm which has known ETA times,
//so a thread which knows about an upcoming task
//can choose to reject long-running tasks;
//This will require coordination around the workload of other threads.
//If there is already one thread spinning, looking for work,
//and that thread is spending less than X % of time working,
//then we can take on any job.
//There will also be a necessary priority calculation;
//A big pending high priority job should take any live threads,
//and we can spin up more replacements for other tasks if needed.
//In order to prevent attention-starvation, a task's priority will get
//bumped up if it has to wait too long.
//Using a min-max latency and eta will help in determining the best job
//to take.
//First, check for immediate jobs scheduled by the current thread.
//this will help in cases when a thread simply wants to yield,
//or when a forking process wants to continue immediately,
//or when gluing together methods into a process.
//Next, check for high priority jobs scheduled by any thread.
//Anything with a timeout at or near expiration should be taken.
//If there are known tiny jobs available with a known eta less than
//the wait time for the high priority job, that job should be taken as well.
//If no such jobs exist, scan the work queue to elevate any starving tasks
//If no high priority jobs are waiting, scan the work queue for either
//a) work to do; if timeout ~expired, take and run immediately
//b) tasks to elevate;
//the iterator supplied when looking for work should elevate on its own.
//if no task is found, die unless you are the last thread.
//if timeout > now, drain the iterator to make sure anything needing
//elevation gets it.
}
}
protected class EnviroMap extends
AbstractMultiInitMap {
public EnviroMap() {
super(new ConvertsValue() {
@Override
public String convert(Thread from) {
return from.getName();
}
});
}
@Override
protected ConcurrentEnvironment initialize(Thread key, UncaughtExceptionHandler params) {
if (key.getState() == State.TERMINATED) {
//send an exception...
params.uncaughtException(key, new ThreadDeath());
}
if (key.isInterrupted()) {
params.uncaughtException(key, new InterruptedException());
}
X_Log.debug("Initializing Concurrent Environment", key);
return initializeEnvironment(key, params);
}
@Override
protected UncaughtExceptionHandler defaultParams() {
return Thread.currentThread().getUncaughtExceptionHandler();
}
}
protected abstract ConcurrentEnvironment initializeEnvironment(Thread key, UncaughtExceptionHandler params);
protected int threadFlushTime() {
return 2000;
}
private final EnviroMap environments = initMap();
private AtomicInteger threadCount = new AtomicInteger();
@Override
public Thread newThread(Runnable cmd) {
WrappedRunnable wrapped = wrap(cmd);
Thread childThread = new Thread(wrapped);
childThread.setName(cmd.getClass().getName()+"_"+threadCount.incrementAndGet());
Thread running = Thread.currentThread();
ConcurrentEnvironment enviro = environments.get(running, running.getUncaughtExceptionHandler());
enviro.pushThread(childThread);
return childThread;
}
/**
* Allow all subclasses to wrap Runnables for custom behavior.
* @param cmd - The supplied Runnable to execute.
* @return - A wrapped Runnable, or cmd if not desired.
*
* This method is very useful for running benchmarks or testing assertions.
*/
protected WrappedRunnable wrap(Runnable cmd) {
if (cmd instanceof WrappedRunnable)
return (WrappedRunnable)cmd;
return new WrappedRunnable(cmd);
}
protected ConcurrentEnvironment currentEnvironment() {
Thread running = Thread.currentThread();
return environments.get(running, running.getUncaughtExceptionHandler());
}
protected EnviroMap initMap() {
return new EnviroMap();
}
@Override
public ProcessController newProcess(Process process) {
return new ProcessController(process);
}
@Override
public void resolve(final Future future, final ReceivesValue receiver) {
if (future.isDone()) {
callback(future, receiver);
return;
}
//The future isn't done. Let's push a task into the enviro.
Thread otherThread = getFuturesThread();
ConcurrentEnvironment enviro = environments.get(otherThread, otherThread.getUncaughtExceptionHandler());
enviro.monitor(Priority.Low, new Provider() {
@Override
public Boolean get() {
return future.isDone();
}
}, new Runnable() {
@Override
public void run() {
callback(future, receiver);
}
});
}
/**
* Allows multi-threaded environments to have a single thread dedicated to
* monitoring futures for completion.
* @return - The thread to be used for monitoring futures
*/
protected Thread getFuturesThread() {
return Thread.currentThread();
}
protected void callback(Future future, ReceivesValue receiver) {
try {
receiver.set(future.get());
return;
} catch (InterruptedException e) {
debug(e);
Thread.interrupted();
} catch (ExecutionException e) {
debug(e);
throw X_Util.rethrow(X_Util.unwrap(e));
}
}
@Override
@SuppressWarnings("deprecation")
public boolean kill(Thread thread, int timeout) {
if (destroy(thread, timeout))
return true;
try {
thread.interrupt();
return false;
}catch (Exception e) {
thread.stop();
return false;
}
}
private boolean destroy(Thread thread, int timeout) {
if (environments.hasKey(thread.getName())) {
ConcurrentEnvironment enviro = environments.get(thread, thread.getUncaughtExceptionHandler());
boolean success = enviro.destroy(timeout);
environments.removeValue(thread.getName());
return success;
}
return true;
}
@Override
public boolean trySleep(float millis) {
if (Thread.interrupted())
return false;
float leftover = millis - ((int)millis);
try {
Thread.sleep((long)millis, (int)(leftover * 1000000));
return true;
}catch (InterruptedException e) {
Thread.interrupted();
return false;
}
}
@Override
public boolean flush(Thread thread, int timeout) {
ConcurrentEnvironment enviro = environments.getValue(thread.getName());
if (thread == Thread.currentThread()) {
if (enviro == null)
return true;// nothin' to do here!
long deadline = System.currentTimeMillis()+timeout;
while (enviro.flush((int)(deadline-System.currentTimeMillis()))) {
int timeLeft = (int)(deadline-System.currentTimeMillis());
if (timeLeft<1)
return false;
//join a thread, if available
Iterator iter = enviro.getThreads().iterator();
try {
Thread next;
synchronized (enviro) {
if (iter.hasNext()) {
next = iter.next();
iter.remove();
}else
return true;
}
if (next != null)
next.join(timeLeft);
} catch (InterruptedException e) {
destroy(Thread.currentThread(), timeLeft);
Thread.currentThread().interrupt();
}
if (System.currentTimeMillis()>deadline)
return false;
}
return true;
}else {
if (enviro != null)
enviro.scheduleFlush(timeout);
return false;
}
}
@Override
public double now() {
return System.currentTimeMillis();
}
@Override
public double threadStartTime(Thread thread) {
return environments.get(thread, thread.getUncaughtExceptionHandler()).startTime();
}
public boolean isMultiThreaded() {
return X_Runtime.isMultithreaded();
}
@Override
public void runDeferred(Runnable cmd) {
ConcurrentEnvironment enviro = currentEnvironment();
enviro.pushDeferred(cmd);
}
@Override
public void runEventually(Runnable cmd) {
ConcurrentEnvironment enviro = currentEnvironment();
enviro.pushEventually(cmd);
}
@Override
public void runFinally(Runnable cmd) {
ConcurrentEnvironment enviro = currentEnvironment();
enviro.pushFinally(cmd);
}
}