com.ajjpj.afoundation.concurrent.AFutureImpl Maven / Gradle / Ivy
The newest version!
package com.ajjpj.afoundation.concurrent;
import com.ajjpj.afoundation.collection.immutable.AList;
import com.ajjpj.afoundation.collection.immutable.AOption;
import com.ajjpj.afoundation.collection.tuples.ATuple2;
import com.ajjpj.afoundation.collection.tuples.ATuple3;
import com.ajjpj.afoundation.function.AFunction1;
import com.ajjpj.afoundation.function.AStatement1NoThrow;
import com.ajjpj.afoundation.function.AStatement2NoThrow;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author arno
*/
@SuppressWarnings ("Convert2Lambda")
class AFutureImpl extends FutureTask implements AFuture {
private final AtomicReference>> onFinishedListeners = new AtomicReference<> (AList.>nil ());
private final ATaskScheduler threadPool;
private static final Callable NO_CALLABLE = new Callable () {
@Override public Object call () throws Exception {
return null;
}
};
AFutureImpl unscheduled () {
return unscheduled (threadPool);
}
@SuppressWarnings ("unchecked")
static AFutureImpl unscheduled (ATaskScheduler threadPool) {
return new AFutureImpl<> (threadPool, NO_CALLABLE);
}
AFutureImpl (ATaskScheduler threadPool, Callable callable) {
super (callable);
this.threadPool = threadPool;
}
AFutureImpl (ATaskScheduler threadPool, Runnable runnable, T value) {
super (runnable, value);
this.threadPool = threadPool;
}
@Override public AFuture withDefaultValue (final T defaultValue) {
final AFutureImpl result = unscheduled ();
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 == null) {
result.set (param1);
}
else {
result.set (defaultValue);
}
}
});
return result;
}
@Override protected void set (T t) {
super.set (t);
}
@Override protected void setException (Throwable t) {
super.setException (t);
}
void setTimedOut() {
setException (new TimeoutException ());
}
@Override public boolean isFinished () {
return super.isDone ();
}
@Override protected void done () {
notifyListeners ();
}
private void notifyListeners () {
T result = null;
Throwable th = null;
try {
result = get ();
}
catch (Exception exc) {
th = exc;
}
// This method can be called several times, even concurrently - see the comment in onFinished(). Atomically removing all listeners from the list deals with
// that concurrency in a robust fashion.
for (AStatement2NoThrow l: onFinishedListeners.getAndSet (AList.>nil())) {
l.apply (result, th);
}
}
@Override public void onSuccess (final AStatement1NoThrow callback) {
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 == null) {
callback.apply (param1);
}
}
});
}
@Override public void onFailure (final AStatement1NoThrow callback) {
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 != null) {
callback.apply (param2);
}
}
});
}
@Override public void onFinished (AStatement2NoThrow callback) {
AList> prev;
do {
prev = onFinishedListeners.get ();
}
while (! onFinishedListeners.compareAndSet (prev, prev.cons (callback)));
if (isDone ()) {
// This is necessary for notifying listeners that are registered *after* the future is done.
// NB: Doing it this way takes care of the situation that the future is not done at the beginning of onFinished() but becomes so before the listener is registered.
notifyListeners ();
}
}
@SuppressWarnings ("unchecked")
@Override public AFuture mapSync (final AFunction1 f) {
final AFutureImpl result = new AFutureImpl<> (threadPool, NO_CALLABLE);
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 != null) {
if (param2 instanceof ExecutionException) {
final ExecutionException ee = (ExecutionException) param2;
if (ee.getCause () != null) {
param2 = ee.getCause ();
}
}
result.setException (param2);
return;
}
try {
result.set (f.apply (param1));
}
catch (Exception e) {
result.setException (e);
}
}
});
return result;
}
@Override public AFuture mapAsync (AFunction1 f, long timeout, TimeUnit timeoutUnit) {
return mapAsync (f, threadPool, timeout, timeoutUnit);
}
@SuppressWarnings ("unchecked")
@Override public AFuture mapAsync (final AFunction1 f, final ATaskScheduler threadPool, final long timeout, final TimeUnit timeoutUnit) {
final AFutureImpl result = new AFutureImpl<> (threadPool, NO_CALLABLE);
onFinished (new AStatement2NoThrow () {
@Override public void apply (final T param1, Throwable param2) {
if (param2 != null) {
if (param2 instanceof ExecutionException) {
final ExecutionException ee = (ExecutionException) param2;
if (ee.getCause () != null) {
param2 = ee.getCause ();
}
}
result.setException (param2);
return;
}
try {
final AFuture mappedFuture = threadPool.submit (new Callable () {
@Override public U call () throws Exception {
return f.apply (param1);
}
}, timeout, timeoutUnit);
mappedFuture.onFinished (new AStatement2NoThrow () {
@Override public void apply (U param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
result.set (param1);
}
}
});
}
catch (Throwable exc) {
result.setException (exc);
}
}
});
return result;
}
@SuppressWarnings ("unchecked")
@Override public AFuture> zip (AFuture other) {
final AFutureImpl> result = new AFutureImpl<> (threadPool, NO_CALLABLE);
final ResultCollector collector = new ResultCollector<> ();
collector.set3 (collector);
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
final boolean finished = collector.set1 (param1);
if (finished) {
result.set (new ATuple2<> (collector._1.get (), collector._2.get ()));
}
}
}
});
other.onFinished (new AStatement2NoThrow () {
@Override public void apply (U param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
final boolean finished = collector.set2 (param1);
if (finished) {
result.set (new ATuple2<> (collector._1.get (), collector._2.get ()));
}
}
}
});
return result;
}
@SuppressWarnings ("unchecked")
@Override public AFuture> zip (AFuture other1, AFuture other2) {
final AFutureImpl> result = new AFutureImpl<> (threadPool, NO_CALLABLE);
final ResultCollector collector = new ResultCollector<> ();
onFinished (new AStatement2NoThrow () {
@Override public void apply (T param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
final boolean finished = collector.set1 (param1);
if (finished) {
result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
}
}
}
});
other1.onFinished (new AStatement2NoThrow () {
@Override public void apply (U param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
final boolean finished = collector.set2 (param1);
if (finished) {
result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
}
}
}
});
other2.onFinished (new AStatement2NoThrow () {
@Override public void apply (V param1, Throwable param2) {
if (param2 != null) {
result.setException (param2);
}
else {
final boolean finished = collector.set3 (param1);
if (finished) {
result.set (new ATuple3<> (collector._1.get (), collector._2.get (), collector._3.get ()));
}
}
}
});
return result;
}
static class ResultCollector {
private AOption _1 = AOption.none ();
private AOption _2 = AOption.none ();
private AOption _3 = AOption.none ();
synchronized boolean set1 (R o) {
_1 = AOption.some (o);
return _2.isDefined () && _3.isDefined ();
}
synchronized boolean set2 (S o) {
_2 = AOption.some (o);
return _1.isDefined () && _3.isDefined ();
}
synchronized boolean set3 (T o) {
_3 = AOption.some (o);
return _1.isDefined () && _2.isDefined ();
}
}
}