Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail fast implementation for joinList #68

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/main/java/com/spotify/futures/CompletableFutures.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private CompletableFutures() {
* Returns a new {@link CompletableFuture} which completes to a list of all values of its input
* stages, if all succeed. The list of results is in the same order as the input stages.
*
* <p> If any of the given stages complete exceptionally, then the returned future also does so,
* <p> As soon as any of the given stages complete exceptionally, then the returned future also does so,
* with a {@link CompletionException} holding this exception as its cause.
*
* <p> If no stages are provided, returns a future holding an empty list.
Expand All @@ -75,7 +75,19 @@ public static <T> CompletableFuture<List<T>> allAsList(
for (int i = 0; i < stages.size(); i++) {
all[i] = stages.get(i).toCompletableFuture();
}
return CompletableFuture.allOf(all)

CompletableFuture<Void> allOf = CompletableFuture.allOf(all);

for (int i = 0; i < all.length; i++) {
all[i].exceptionally(throwable -> {
if (!allOf.isDone()) {
allOf.completeExceptionally(throwable);
}
return null; // intentionally unused
});
}

return allOf
.thenApply(ignored -> {
final List<T> result = new ArrayList<>(all.length);
for (int i = 0; i < all.length; i++) {
Expand Down
21 changes: 21 additions & 0 deletions src/test/java/com/spotify/futures/CompletableFuturesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;

import static com.spotify.futures.CompletableFutures.allAsList;
import static com.spotify.futures.CompletableFutures.allAsMap;
Expand Down Expand Up @@ -64,13 +66,15 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.Is.isA;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
Expand Down Expand Up @@ -125,6 +129,23 @@ public void allAsList_exceptional() throws Exception {
allAsList(input).get();
}

@Test
public void allAsList_exceptional_failFast() {
final CompletableFuture<String> incomplete = incompleteFuture();
final CompletableFuture<String> failed =
exceptionallyCompletedFuture(new TimeoutException());
final List<CompletionStage<String>> input =
asList(incomplete, failed);

try {
allAsList(input).join();
fail("Expected exception being thrown.");
} catch (Exception e) {
assertThat(e, instanceOf(CompletionException.class));
assertThat(e.getCause(), instanceOf(TimeoutException.class));
}
}

@Test
public void allAsList_null() throws Exception {
exception.expect(NullPointerException.class);
Expand Down