com.outbrain.ob1k.concurrent.eager.EagerComposableFuture Maven / Gradle / Ivy
package com.outbrain.ob1k.concurrent.eager;
import com.google.common.base.Function;
import com.outbrain.ob1k.concurrent.*;
import com.outbrain.ob1k.concurrent.handlers.*;
import com.outbrain.ob1k.concurrent.Producer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* User: aronen
* Date: 6/6/13
* Time: 2:08 PM
*/
public final class EagerComposableFuture implements ComposableFuture, ComposablePromise {
private static final Logger logger = LoggerFactory.getLogger(EagerComposableFuture.class);
private final Executor threadPool;
private final HandlersList handlers;
private final AtomicReference> value = new AtomicReference<>();
public EagerComposableFuture() {
threadPool = null;
handlers = new HandlersList();
}
public EagerComposableFuture(final Executor threadPool) {
this.threadPool = threadPool;
handlers = new HandlersList();
}
@Override
public void set(final T result) {
if (value.compareAndSet(null, Try.fromValue(result))) {
done();
}
}
@Override
public void setException(final Throwable t) {
if (value.compareAndSet(null, Try.fromError(t))) {
done();
}
}
@Override
public ComposableFuture future() {
return this;
}
public static ComposableFuture fromValue(final T value) {
final EagerComposableFuture result = new EagerComposableFuture<>();
result.set(value);
return result;
}
public static ComposableFuture fromError(final Throwable error) {
final EagerComposableFuture result = new EagerComposableFuture<>();
result.setException(error);
return result;
}
public static ComposableFuture build(final Producer producer) {
final EagerComposableFuture future = new EagerComposableFuture<>();
producer.produce(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
future.setException(result.getError());
}
}
});
return future;
}
public static ComposableFuture submit(final Executor executor, final Callable task, final boolean delegateHandler) {
if (task == null)
return fromError(new NullPointerException("task must not be null"));
final EagerComposableFuture future = delegateHandler ?
new EagerComposableFuture(executor) :
new EagerComposableFuture();
executor.execute(new Runnable() {
@Override
public void run() {
try {
future.set(task.call());
} catch (final Exception e) {
future.setException(e);
}
}
});
return future;
}
public static ComposableFuture schedule(final Scheduler scheduler, final Callable task, final long delay, final TimeUnit unit) {
final EagerComposableFuture res = new EagerComposableFuture<>();
scheduler.schedule(new Runnable() {
@Override
public void run() {
try {
res.set(task.call());
} catch (final Exception e) {
res.setException(e);
}
}
}, delay, unit);
return res;
}
public static ComposableFuture doubleDispatch(final FutureAction action, final long duration,
final TimeUnit unit, final Scheduler scheduler) {
final ComposableFuture first = action.execute();
final AtomicBoolean done = new AtomicBoolean();
first.consume(new Consumer() {
@Override
public void consume(final Try result) {
done.compareAndSet(false, true);
}
});
final EagerComposableFuture second = new EagerComposableFuture<>();
scheduler.schedule(new Runnable() {
@Override
public void run() {
if (done.compareAndSet(false, true)) {
try {
final ComposableFuture innerSecond = action.execute();
innerSecond.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
second.set(result.getValue());
} else {
second.setException(result.getError());
}
}
});
} catch (final Exception e) {
second.setException(e);
}
}
}
}, duration, unit);
return collectFirst(Arrays.asList(first, second));
}
public static ComposableFuture collectFirst(final List> futures) {
final int size = futures.size();
if (size == 0) {
return fromError(new IllegalArgumentException("empty future list"));
}
final EagerComposableFuture res = new EagerComposableFuture<>();
final AtomicBoolean done = new AtomicBoolean();
for (final ComposableFuture future : futures) {
future.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (done.compareAndSet(false, true)) {
if (result.isSuccess()) {
res.set(result.getValue());
} else {
res.setException(result.getError());
}
}
}
});
}
return res;
}
private void done() {
handlers.execute(threadPool);
}
@Override
public ComposableFuture continueWith(final FutureResultHandler handler) {
final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try res) {
try {
final ComposableFuture nextResult = handler.handle(res);
if (nextResult == null) {
future.set(null);
} else {
nextResult.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
future.setException(result.getError());
}
}
});
}
} catch (final Exception e) {
future.setException(e);
}
}
});
return future;
}
@Override
public ComposableFuture continueWith(final ResultHandler handler) {
final EagerComposableFuture result = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try res) {
try {
result.set(handler.handle(res));
} catch (final Exception e) {
result.setException(e);
}
}
});
return result;
}
@Override
public ComposableFuture continueOnSuccess(final FutureSuccessHandler super T, R> handler) {
final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
try {
final ComposableFuture res = handler.handle(result.getValue());
if (res == null) {
future.set(null);
} else {
res.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
future.setException(result.getError());
}
}
});
}
} catch (final Exception e) {
future.setException(e);
}
} else {
future.setException(result.getError());
}
}
});
return future;
}
@Override
public ComposableFuture continueOnSuccess(final SuccessHandler super T, ? extends R> handler) {
final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
try {
future.set(handler.handle(result.getValue()));
} catch (final ExecutionException e) {
future.setException(e.getCause() != null ? e.getCause() : e);
} catch (final Exception e) {
future.setException(e);
}
} else {
future.setException(result.getError());
}
}
});
return future;
}
@Override
public ComposableFuture continueOnError(final FutureErrorHandler handler) {
final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
try {
final ComposableFuture res = handler.handle(result.getError());
if (res == null) {
future.set(null);
} else {
res.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
future.setException(result.getError());
}
}
});
}
} catch (final Exception e) {
future.setException(e);
}
}
}
});
return future;
}
@Override
public ComposableFuture continueOnError(final ErrorHandler extends T> handler) {
final EagerComposableFuture future = new EagerComposableFuture<>(threadPool);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
if (result.isSuccess()) {
future.set(result.getValue());
} else {
try {
future.set(handler.handle(result.getError()));
} catch (final ExecutionException e) {
future.setException(e.getCause() != null ? e.getCause() : e);
} catch (final Exception e) {
future.setException(e);
}
}
}
});
return future;
}
@Override
public void consume(final Consumer consumer) {
handlers.addHandler(new ConsumerAction<>(consumer, this), threadPool);
}
@Override
public ComposableFuture transform(final Function super T, ? extends R> function) {
return continueOnSuccess(new SuccessHandler() {
@Override
public R handle(final T result) {
return function.apply(result);
}
});
}
@Override
public ComposableFuture withTimeout(final long duration, final TimeUnit unit, final String taskDescription) {
return withTimeout(ComposableFutures.getScheduler(), duration, unit, taskDescription);
}
@Override
public ComposableFuture withTimeout(final Scheduler scheduler, final long timeout, final TimeUnit unit, final String taskDescription) {
final ComposablePromise deadline = new EagerComposableFuture<>();
final CancellationToken cancellationToken = scheduler.schedule(new Runnable() {
@Override
public void run() {
deadline.setException(new TimeoutException("Timeout occurred on task ('" + taskDescription + "' " + timeout + " " + unit + ")"));
}
}, timeout, unit);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
cancellationToken.cancel(false);
}
});
return collectFirst(Arrays.asList(this, deadline.future()));
}
@Override
public ComposableFuture withTimeout(final long duration, final TimeUnit unit) {
return withTimeout(ComposableFutures.getScheduler(), duration, unit);
}
@Override
public ComposableFuture withTimeout(final Scheduler scheduler, final long timeout, final TimeUnit unit) {
return withTimeout(scheduler, timeout, unit, "unspecified task");
}
@Override
public ComposableFuture materialize() {
return this;
}
@Override
public T get() throws InterruptedException, ExecutionException {
final CountDownLatch latch = new CountDownLatch(1);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
latch.countDown();
}
});
latch.await();
final Try currentValue = this.value.get();
if (currentValue.isSuccess()) {
return currentValue.getValue();
} else {
throw new ExecutionException(currentValue.getError());
}
}
@Override
public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
final CountDownLatch latch = new CountDownLatch(1);
this.consume(new Consumer() {
@Override
public void consume(final Try result) {
latch.countDown();
}
});
if (latch.await(timeout, unit)) {
final Try currentValue = this.value.get();
if (currentValue.isSuccess()) {
return currentValue.getValue();
} else {
throw new ExecutionException(currentValue.getError());
}
} else {
throw new TimeoutException("Timeout occurred while waiting for value (" + timeout + unit + ")");
}
}
private static class ConsumerAction implements Runnable {
final Consumer inner;
final EagerComposableFuture current;
private ConsumerAction(final Consumer inner, final EagerComposableFuture current) {
this.inner = inner;
this.current = current;
}
@Override
public void run() {
try {
final Try currentValue = current.value.get();
inner.consume(currentValue);
} catch (final Throwable error) {
logger.warn("error while handling future callbacks", error);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy