Skip to content

Commit

Permalink
Add Futures.getDone().
Browse files Browse the repository at this point in the history
API Review:
https://docs.google.com/document/d/1HvGIffjs3clWLzs0SNzLSKSzlzjIIesZOt6fhedo85Y/edit

Also, make immediateCancelledFuture() available under GWT, since I use it in the new tests.
To use cancellationExceptionWithCause() from it, I had to move that method out of the server-only AbstractFuture.java and into the shared Futures.java.
This runs somewhat counter to my effort to make Futures.java smaller. Maybe we'll want a Util/Helpers class, though I'm not quite sure what else would go in it (executeListener? currently non-static maybePropagateCancellation?).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=116279071
  • Loading branch information
cpovirk committed Mar 3, 2016
1 parent 6daefa4 commit 3875a1b
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2008 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.util.concurrent;
public class FuturesGetDoneTest_gwt extends com.google.gwt.junit.client.GWTTestCase {
@Override public String getModuleName() {
return "com.google.common.util.concurrent.testModule";
}
public void testCancelled() throws Exception {
com.google.common.util.concurrent.FuturesGetDoneTest testCase = new com.google.common.util.concurrent.FuturesGetDoneTest();
testCase.testCancelled();
}

public void testFailed() throws Exception {
com.google.common.util.concurrent.FuturesGetDoneTest testCase = new com.google.common.util.concurrent.FuturesGetDoneTest();
testCase.testFailed();
}

public void testPending() throws Exception {
com.google.common.util.concurrent.FuturesGetDoneTest testCase = new com.google.common.util.concurrent.FuturesGetDoneTest();
testCase.testPending();
}

public void testSuccessful() throws Exception {
com.google.common.util.concurrent.FuturesGetDoneTest testCase = new com.google.common.util.concurrent.FuturesGetDoneTest();
testCase.testSuccessful();
}

public void testSuccessfulNull() throws Exception {
com.google.common.util.concurrent.FuturesGetDoneTest testCase = new com.google.common.util.concurrent.FuturesGetDoneTest();
testCase.testSuccessfulNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,33 @@ public void testDereference_resultCancelsOuter() throws Exception {
}
}

public void testImmediateCancelledFutureBasic() throws Exception {
com.google.common.util.concurrent.FuturesTest testCase = new com.google.common.util.concurrent.FuturesTest();
testCase.setUp();
Throwable failure = null;
try {
testCase.testImmediateCancelledFutureBasic();
} catch (Throwable t) {
failure = t;
}
try {
testCase.tearDown();
} catch (Throwable t) {
if (failure == null) {
failure = t;
}
}
if (failure instanceof Exception) {
throw (Exception) failure;
}
if (failure instanceof Error) {
throw (Error) failure;
}
if (failure != null) {
throw new RuntimeException(failure);
}
}

public void testImmediateFailedFuture() throws Exception {
com.google.common.util.concurrent.FuturesTest testCase = new com.google.common.util.concurrent.FuturesTest();
testCase.setUp();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2016 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.common.util.concurrent;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.Futures.getDone;
import static com.google.common.util.concurrent.Futures.immediateCancelledFuture;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;

import com.google.common.annotations.GwtCompatible;

import junit.framework.TestCase;

import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

/**
* Unit tests for {@link Futures#getDone}.
*/
@GwtCompatible
public class FuturesGetDoneTest extends TestCase {
public void testSuccessful() throws ExecutionException {
assertThat(getDone(immediateFuture("a"))).isEqualTo("a");
}

public void testSuccessfulNull() throws ExecutionException {
assertThat(getDone(immediateFuture((String) null))).isEqualTo(null);
}

public void testFailed() {
Exception failureCause = new Exception();
try {
getDone(immediateFailedFuture(failureCause));
fail();
} catch (ExecutionException expected) {
assertThat(expected.getCause()).isEqualTo(failureCause);
}
}

public void testCancelled() throws ExecutionException {
try {
getDone(immediateCancelledFuture());
fail();
} catch (CancellationException expected) {
}
}

public void testPending() throws ExecutionException {
try {
getDone(SettableFuture.create());
fail();
} catch (IllegalStateException expected) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,13 @@ public void testImmediateFailedFuture_cancellationException() throws Exception {
}
}

@GwtIncompatible // immediateCancelledFuture
public void testImmediateCancelledFuture() throws Exception {
public void testImmediateCancelledFutureBasic() throws Exception {
ListenableFuture<String> future = CallerClass1.immediateCancelledFuture();
assertTrue(future.isCancelled());
}

@GwtIncompatible
public void testImmediateCancelledFutureStack() throws Exception {
ListenableFuture<String> future = CallerClass1.immediateCancelledFuture();
assertTrue(future.isCancelled());

Expand Down Expand Up @@ -185,15 +190,12 @@ public boolean apply(StackTraceElement element) {
};
}

@GwtIncompatible // used only in GwtIncompatible tests
private static final class CallerClass1 {

static ListenableFuture<String> immediateCancelledFuture() {
return Futures.immediateCancelledFuture();
}
}

@GwtIncompatible // used only in GwtIncompatible tests
private static final class CallerClass2 {
@CanIgnoreReturnValue
static <V> V get(ListenableFuture<V> future) throws ExecutionException, InterruptedException {
Expand All @@ -202,7 +204,6 @@ static <V> V get(ListenableFuture<V> future) throws ExecutionException, Interrup
}

private static class MyException extends Exception {

}

@GwtIncompatible // immediateCheckedFuture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.common.util.concurrent;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.cancellationExceptionWithCause;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;

Expand Down Expand Up @@ -847,13 +848,6 @@ private static void executeListener(Runnable runnable, Executor executor) {
}
}

static final CancellationException cancellationExceptionWithCause(
@Nullable String message, @Nullable Throwable cause) {
CancellationException exception = new CancellationException(message);
exception.initCause(cause);
return exception;
}

private abstract static class AtomicHelper {
/** Non volatile write of the thread to the {@link Waiter#thread} field. */
abstract void putThread(Waiter waiter, Thread newValue);
Expand Down
45 changes: 44 additions & 1 deletion guava/src/com/google/common/util/concurrent/Futures.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.common.util.concurrent;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;

Expand Down Expand Up @@ -178,7 +179,6 @@ public static <V> ListenableFuture<V> immediateFailedFuture(Throwable throwable)
*
* @since 14.0
*/
@GwtIncompatible // TODO
public static <V> ListenableFuture<V> immediateCancelledFuture() {
return new ImmediateCancelledFuture<V>();
}
Expand Down Expand Up @@ -1135,6 +1135,42 @@ public void run() {
future.addListener(callbackListener, executor);
}

/**
* Returns the result of the input {@code Future}, which must have already completed.
*
* <p>The benefits of this method are twofold. First, the name "getDone" suggests to readers that
* the {@code Future} is already done. Second, if buggy code calls {@code getDone} on a {@code
* Future} that is still pending, the program will throw instead of block. This can be important
* for APIs like {@link whenAllComplete whenAllComplete(...)}{@code .}{@link
* FutureCombiner#call(Callable) call(...)}, where it is easy to use a new input from the {@code
* call} implementation but forget to add it to the arguments of {@code whenAllComplete}.
*
* <p>If you are looking for a method to determine whether a given {@code Future} is done, use the
* instance method {@link Future#isDone()}.
*
* @throws ExecutionException if the {@code Future} failed with an exception
* @throws CancellationException if the {@code Future} was cancelled
* @throws IllegalStateException if the {@code Future} is not done
* @since 20.0
*/
@CanIgnoreReturnValue
// TODO(cpovirk): Consider calling getDone() in our own code.
public static <V> V getDone(Future<V> future) throws ExecutionException {
/*
* We throw IllegalStateException, since the call could succeed later. Perhaps we "should" throw
* IllegalArgumentException, since the call could succeed with a different argument. Those
* exceptions' docs suggest that either is acceptable. Google's Java Practices page recommends
* IllegalArgumentException here, in part to keep its recommendation simple: Static methods
* should throw IllegalStateException only when they use static state.
*
*
* Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same
* exception as Futures.getDone(fluentFuture).
*/
checkState(future.isDone());
return getUninterruptibly(future);
}

/**
* Returns the result of {@link Future#get()}, converting most exceptions to a new instance of the
* given checked exception type. This reduces boilerplate for a common use of {@code Future} in
Expand Down Expand Up @@ -1321,4 +1357,11 @@ protected X mapException(Exception e) {
return mapper.apply(e);
}
}

static final CancellationException cancellationExceptionWithCause(
@Nullable String message, @Nullable Throwable cause) {
CancellationException exception = new CancellationException(message);
exception.initCause(cause);
return exception;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.common.util.concurrent;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.cancellationExceptionWithCause;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
Expand Down Expand Up @@ -134,7 +135,6 @@ public V get() throws ExecutionException {
}
}

@GwtIncompatible // TODO
static class ImmediateCancelledFuture<V> extends ImmediateFuture<V> {
private final CancellationException thrown;

Expand All @@ -149,7 +149,7 @@ public boolean isCancelled() {

@Override
public V get() {
throw AbstractFuture.cancellationExceptionWithCause("Task was cancelled.", thrown);
throw cancellationExceptionWithCause("Task was cancelled.", thrown);
}
}

Expand Down

0 comments on commit 3875a1b

Please sign in to comment.