package ini.trakem2.utils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
public class TaskOnEDT<V> implements Future<V> {
private V result;
private Callable<V> fn;
private AtomicBoolean started = new AtomicBoolean(false);
/** The task @param fn should not be threaded; this class is intended to run
* small snippets of code under the event dispatch thread, while still being
* able to retrieve the result of the computation. */
public TaskOnEDT(final Callable<V> fn) {
this.fn = fn;
}
/** Will only prevent execution, not interrupt it if its happening.
* @return true if the task didn't start yet. */
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
synchronized (this) {
fn = null;
return !started.get();
}
}
@SuppressWarnings("unchecked")
@Override
public V get() throws InterruptedException, ExecutionException {
if (null != result) return result;
final V[] v = (V[])new Object[1];
final ExecutionException[] ee = new ExecutionException[1];
final AtomicBoolean launched = new AtomicBoolean(false);
Utils.invokeLater(new Runnable() {
public void run() {
launched.set(true);
synchronized (v) {
final Callable<V> c;
synchronized (TaskOnEDT.this) {
c = fn;
if (null == c) return;
started.set(true);
}
try {
v[0] = c.call();
} catch (Throwable t) {
ee[0] = new ExecutionException(t);
}
}
}
});
// Wait until the event dispatch thread runs the Runnable.
// (Or it gets run immediately if get() is called from within the event dispatch thread.)
while (!launched.get()) try { Thread.sleep(5); } catch (InterruptedException ie) {}
// Block until the computation is done
synchronized (v) {
if (null != ee[0]) throw ee[0];
result = v[0];
return result;
}
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
new Thread() {
{ setPriority(Thread.NORM_PRIORITY); }
public void run() {
try {
result = get();
} catch (Throwable t) {
IJError.print(t);
}
}
}.start();
final long end = System.currentTimeMillis() + unit.toMillis(timeout);
final long period = timeout < 200 ? timeout : 200;
while (null == result && System.currentTimeMillis() < end) {
Thread.sleep(period);
}
return result;
}
@Override
public boolean isCancelled() {
synchronized (this) {
return null == fn;
}
}
@Override
public boolean isDone() {
return null != result;
}
}