CompletableFuture & CompletionStage integration. #500

pull/802/merge
Nikita 9 years ago
parent 45a0989bd1
commit 27209e88f2

@ -15,6 +15,7 @@
*/ */
package org.redisson.api; package org.redisson.api;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
@ -26,7 +27,7 @@ import io.netty.util.concurrent.FutureListener;
* *
* @param <V> * @param <V>
*/ */
public interface RFuture<V> extends java.util.concurrent.Future<V> { public interface RFuture<V> extends java.util.concurrent.Future<V>, CompletionStage<V> {
/** /**
* Returns {@code true} if and only if the I/O operation was completed * Returns {@code true} if and only if the I/O operation was completed
@ -52,6 +53,22 @@ public interface RFuture<V> extends java.util.concurrent.Future<V> {
*/ */
V getNow(); V getNow();
/**
* Returns the result value when complete, or throws an
* (unchecked) exception if completed exceptionally. To better
* conform with the use of common functional forms, if a
* computation involved in the completion of this
* CompletableFuture threw an exception, this method throws an
* (unchecked) {@link CompletionException} with the underlying
* exception as its cause.
*
* @return the result value
* @throws CancellationException if the computation was cancelled
* @throws CompletionException if this future completed
* exceptionally or a completion computation threw an exception
*/
V join();
/** /**
* Waits for this future to be completed within the * Waits for this future to be completed within the
* specified time limit. * specified time limit.

@ -15,9 +15,16 @@
*/ */
package org.redisson.misc; package org.redisson.misc;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
@ -34,120 +41,261 @@ public class PromiseDelegator<T> implements RPromise<T> {
return promise; return promise;
} }
@Override public T join() {
return promise.join();
}
public boolean isSuccess() { public boolean isSuccess() {
return promise.isSuccess(); return promise.isSuccess();
} }
@Override
public boolean trySuccess(T result) { public boolean trySuccess(T result) {
return promise.trySuccess(result); return promise.trySuccess(result);
} }
@Override
public Throwable cause() { public Throwable cause() {
return promise.cause(); return promise.cause();
} }
@Override public T getNow() {
return promise.getNow();
}
public boolean tryFailure(Throwable cause) { public boolean tryFailure(Throwable cause) {
return promise.tryFailure(cause); return promise.tryFailure(cause);
} }
@Override public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return promise.await(timeout, unit);
}
public boolean setUncancellable() { public boolean setUncancellable() {
return promise.setUncancellable(); return promise.setUncancellable();
} }
@Override public boolean await(long timeoutMillis) throws InterruptedException {
return promise.await(timeoutMillis);
}
public RPromise<T> addListener(FutureListener<? super T> listener) { public RPromise<T> addListener(FutureListener<? super T> listener) {
return promise.addListener(listener); return promise.addListener(listener);
} }
@Override
public RPromise<T> addListeners(FutureListener<? super T>... listeners) { public RPromise<T> addListeners(FutureListener<? super T>... listeners) {
return promise.addListeners(listeners); return promise.addListeners(listeners);
} }
@Override
public RPromise<T> removeListener(FutureListener<? super T> listener) { public RPromise<T> removeListener(FutureListener<? super T> listener) {
return promise.removeListener(listener); return promise.removeListener(listener);
} }
@Override
public RPromise<T> removeListeners(FutureListener<? super T>... listeners) { public RPromise<T> removeListeners(FutureListener<? super T>... listeners) {
return promise.removeListeners(listeners); return promise.removeListeners(listeners);
} }
@Override
public RPromise<T> await() throws InterruptedException { public RPromise<T> await() throws InterruptedException {
return promise.await(); return promise.await();
} }
@Override public boolean cancel(boolean mayInterruptIfRunning) {
return promise.cancel(mayInterruptIfRunning);
}
public RPromise<T> awaitUninterruptibly() { public RPromise<T> awaitUninterruptibly() {
return promise.awaitUninterruptibly(); return promise.awaitUninterruptibly();
} }
@Override
public RPromise<T> sync() throws InterruptedException { public RPromise<T> sync() throws InterruptedException {
return promise.sync(); return promise.sync();
} }
@Override
public RPromise<T> syncUninterruptibly() { public RPromise<T> syncUninterruptibly() {
return promise.syncUninterruptibly(); return promise.syncUninterruptibly();
} }
@Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return promise.await(timeout, unit);
}
@Override
public boolean isCancelled() { public boolean isCancelled() {
return promise.isCancelled(); return promise.isCancelled();
} }
@Override
public boolean isDone() { public boolean isDone() {
return promise.isDone(); return promise.isDone();
} }
@Override
public boolean await(long timeoutMillis) throws InterruptedException {
return promise.await(timeoutMillis);
}
@Override
public T get() throws InterruptedException, ExecutionException { public T get() throws InterruptedException, ExecutionException {
return promise.get(); return promise.get();
} }
@Override
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
return promise.awaitUninterruptibly(timeout, unit); return promise.awaitUninterruptibly(timeout, unit);
} }
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return promise.get(timeout, unit); return promise.get(timeout, unit);
} }
@Override public <U> CompletionStage<U> thenApply(Function<? super T, ? extends U> fn) {
return promise.thenApply(fn);
}
public boolean awaitUninterruptibly(long timeoutMillis) { public boolean awaitUninterruptibly(long timeoutMillis) {
return promise.awaitUninterruptibly(timeoutMillis); return promise.awaitUninterruptibly(timeoutMillis);
} }
@Override public <U> CompletionStage<U> thenApplyAsync(Function<? super T, ? extends U> fn) {
public T getNow() { return promise.thenApplyAsync(fn);
return promise.getNow();
} }
@Override public <U> CompletionStage<U> thenApplyAsync(Function<? super T, ? extends U> fn, Executor executor) {
public boolean cancel(boolean mayInterruptIfRunning) { return promise.thenApplyAsync(fn, executor);
return promise.cancel(mayInterruptIfRunning);
} }
public CompletionStage<Void> thenAccept(Consumer<? super T> action) {
return promise.thenAccept(action);
}
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action) {
return promise.thenAcceptAsync(action);
}
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) {
return promise.thenAcceptAsync(action, executor);
}
public CompletionStage<Void> thenRun(Runnable action) {
return promise.thenRun(action);
}
public CompletionStage<Void> thenRunAsync(Runnable action) {
return promise.thenRunAsync(action);
}
public CompletionStage<Void> thenRunAsync(Runnable action, Executor executor) {
return promise.thenRunAsync(action, executor);
}
public <U, V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn) {
return promise.thenCombine(other, fn);
}
public <U, V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn) {
return promise.thenCombineAsync(other, fn);
}
public <U, V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn, Executor executor) {
return promise.thenCombineAsync(other, fn, executor);
}
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action) {
return promise.thenAcceptBoth(other, action);
}
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action) {
return promise.thenAcceptBothAsync(other, action);
}
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor) {
return promise.thenAcceptBothAsync(other, action, executor);
}
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action) {
return promise.runAfterBoth(other, action);
}
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action) {
return promise.runAfterBothAsync(other, action);
}
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor) {
return promise.runAfterBothAsync(other, action, executor);
}
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) {
return promise.applyToEither(other, fn);
}
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) {
return promise.applyToEitherAsync(other, fn);
}
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,
Executor executor) {
return promise.applyToEitherAsync(other, fn, executor);
}
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) {
return promise.acceptEither(other, action);
}
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) {
return promise.acceptEitherAsync(other, action);
}
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,
Executor executor) {
return promise.acceptEitherAsync(other, action, executor);
}
public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action) {
return promise.runAfterEither(other, action);
}
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action) {
return promise.runAfterEitherAsync(other, action);
}
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor) {
return promise.runAfterEitherAsync(other, action, executor);
}
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return promise.thenCompose(fn);
}
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
return promise.thenComposeAsync(fn);
}
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
Executor executor) {
return promise.thenComposeAsync(fn, executor);
}
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn) {
return promise.exceptionally(fn);
}
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) {
return promise.whenComplete(action);
}
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) {
return promise.whenCompleteAsync(action);
}
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) {
return promise.whenCompleteAsync(action, executor);
}
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {
return promise.handle(fn);
}
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) {
return promise.handleAsync(fn);
}
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
return promise.handleAsync(fn, executor);
}
public CompletableFuture<T> toCompletableFuture() {
return promise.toCompletableFuture();
}
} }

@ -15,15 +15,20 @@
*/ */
package org.redisson.misc; package org.redisson.misc;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.api.RFuture; import org.redisson.api.RFuture;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
/** /**
* *
@ -31,9 +36,16 @@ import io.netty.util.concurrent.Promise;
* *
* @param <T> * @param <T>
*/ */
public class RedissonPromise<T> implements RPromise<T> { public class RedissonPromise<T> extends CompletableFuture<T> implements RPromise<T> {
private volatile boolean uncancellable;
private final int SUCCESS = 1;
private final int FAILED = 2;
private final int CANCELED = 3;
private final Promise<T> promise = ImmediateEventExecutor.INSTANCE.newPromise(); private final Promise<T> promise = ImmediateEventExecutor.INSTANCE.newPromise();
private final AtomicInteger status = new AtomicInteger();
public RedissonPromise() { public RedissonPromise() {
} }
@ -50,34 +62,51 @@ public class RedissonPromise<T> implements RPromise<T> {
return future; return future;
} }
public Promise<T> getInnerPromise() { public Promise<T> getInnerPromise() {
return promise; return promise;
} }
@Override @Override
public boolean isSuccess() { public boolean isSuccess() {
return promise.isSuccess(); return isDone() && !isCompletedExceptionally();
} }
@Override @Override
public boolean trySuccess(T result) { public synchronized boolean trySuccess(T result) {
return promise.trySuccess(result); if (status.compareAndSet(0, SUCCESS)) {
complete(result);
promise.trySuccess(result);
return true;
}
return false;
} }
@Override @Override
public Throwable cause() { public Throwable cause() {
return promise.cause(); try {
getNow(null);
} catch (CompletionException e) {
return e.getCause();
}
return null;
} }
@Override @Override
public boolean tryFailure(Throwable cause) { public synchronized boolean tryFailure(Throwable cause) {
return promise.tryFailure(cause); if (status.compareAndSet(0, FAILED)) {
completeExceptionally(cause);
promise.tryFailure(cause);
return true;
}
return false;
} }
@Override @Override
public boolean setUncancellable() { public boolean setUncancellable() {
return promise.setUncancellable(); if (!isDone()) {
uncancellable = true;
}
return uncancellable;
} }
@Override @Override
@ -106,76 +135,97 @@ public class RedissonPromise<T> implements RPromise<T> {
@Override @Override
public RPromise<T> await() throws InterruptedException { public RPromise<T> await() throws InterruptedException {
promise.await(); try {
get();
} catch (ExecutionException | CancellationException e) {
// skip
}
return this; return this;
} }
@Override @Override
public RPromise<T> awaitUninterruptibly() { public RPromise<T> awaitUninterruptibly() {
promise.awaitUninterruptibly(); try {
return await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return this; return this;
} }
@Override @Override
public RPromise<T> sync() throws InterruptedException { public RPromise<T> sync() throws InterruptedException {
promise.sync(); try {
get();
} catch (ExecutionException e) {
if (e.getCause() instanceof CancellationException) {
throw (CancellationException)e.getCause();
}
PlatformDependent.throwException(e.getCause());
}
return this; return this;
} }
@Override @Override
public RPromise<T> syncUninterruptibly() { public RPromise<T> syncUninterruptibly() {
promise.syncUninterruptibly(); try {
join();
} catch (CompletionException e) {
PlatformDependent.throwException(e.getCause());
}
return this; return this;
} }
@Override @Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return promise.await(timeout, unit); try {
} get(timeout, unit);
} catch (ExecutionException e) {
@Override if (e.getCause() instanceof CancellationException) {
public boolean isCancelled() { throw (CancellationException)e.getCause();
return promise.isCancelled(); }
} throw new CompletionException(e.getCause());
} catch (TimeoutException e) {
@Override return false;
public boolean isDone() { }
return promise.isDone(); return isDone();
} }
@Override @Override
public boolean await(long timeoutMillis) throws InterruptedException { public boolean await(long timeoutMillis) throws InterruptedException {
return promise.await(timeoutMillis); return await(timeoutMillis, TimeUnit.MILLISECONDS);
}
@Override
public T get() throws InterruptedException, ExecutionException {
return promise.get();
} }
@Override @Override
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
return promise.awaitUninterruptibly(timeout, unit); try {
} return await(timeout, unit);
} catch (InterruptedException e) {
@Override Thread.currentThread().interrupt();
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return false;
return promise.get(timeout, unit); }
} }
@Override @Override
public boolean awaitUninterruptibly(long timeoutMillis) { public boolean awaitUninterruptibly(long timeoutMillis) {
return promise.awaitUninterruptibly(timeoutMillis); return awaitUninterruptibly(timeoutMillis, TimeUnit.MILLISECONDS);
} }
@Override @Override
public T getNow() { public T getNow() {
return promise.getNow(); return getNow(null);
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public synchronized boolean cancel(boolean mayInterruptIfRunning) {
return promise.cancel(mayInterruptIfRunning); if (uncancellable) {
return false;
}
if (status.compareAndSet(0, CANCELED)) {
promise.cancel(mayInterruptIfRunning);
return super.cancel(mayInterruptIfRunning);
}
return false;
} }
} }

@ -33,7 +33,6 @@ import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise; import org.redisson.misc.RedissonPromise;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.ImmediateEventExecutor;
public class RedisClientTest { public class RedisClientTest {
@ -69,12 +68,17 @@ public class RedisClientTest {
public void testConnectAsync() throws InterruptedException { public void testConnectAsync() throws InterruptedException {
RedisClient c = new RedisClient("localhost", 6379); RedisClient c = new RedisClient("localhost", 6379);
RFuture<RedisConnection> f = c.connectAsync(); RFuture<RedisConnection> f = c.connectAsync();
final CountDownLatch l = new CountDownLatch(1); final CountDownLatch l = new CountDownLatch(2);
f.addListener((FutureListener<RedisConnection>) future -> { f.addListener((FutureListener<RedisConnection>) future -> {
RedisConnection conn = future.get(); RedisConnection conn = future.get();
assertThat(conn.sync(RedisCommands.PING)).isEqualTo("PONG"); assertThat(conn.sync(RedisCommands.PING)).isEqualTo("PONG");
l.countDown(); l.countDown();
}); });
f.handle((conn, ex) -> {
assertThat(conn.sync(RedisCommands.PING)).isEqualTo("PONG");
l.countDown();
return null;
});
assertThat(l.await(10, TimeUnit.SECONDS)).isTrue(); assertThat(l.await(10, TimeUnit.SECONDS)).isTrue();
} }

@ -12,6 +12,7 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import org.junit.Assert; import org.junit.Assert;
@ -627,8 +628,14 @@ public class RedissonMapTest extends BaseTest {
map.put(4, 6); map.put(4, 6);
map.put(7, 8); map.put(7, 8);
assertThat(map.fastRemoveAsync(1, 3, 7).get()).isEqualTo(3); CountDownLatch l = new CountDownLatch(1);
Thread.sleep(1); RFuture<Long> future = map.fastRemoveAsync(1, 3, 7);
future.handle((r, ex) -> {
assertThat(r).isEqualTo(3);
l.countDown();
return this;
});
assertThat(future.get()).isEqualTo(3);
assertThat(map.size()).isEqualTo(1); assertThat(map.size()).isEqualTo(1);
} }

Loading…
Cancel
Save