From 5c003d46720b1089391f7f8c7a4e8e2ebf0dff39 Mon Sep 17 00:00:00 2001 From: dl Date: Wed, 21 Jan 2015 09:46:21 +0000 Subject: [PATCH] 8068432: Inconsistent exception handling in CompletableFuture.thenCompose Reviewed-by: psandoz, chegar, martin --- .../util/concurrent/CompletableFuture.java | 10 +- .../ThenComposeExceptionTest.java | 112 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java diff --git a/src/share/classes/java/util/concurrent/CompletableFuture.java b/src/share/classes/java/util/concurrent/CompletableFuture.java index 8476dcc12..955daabed 100644 --- a/src/share/classes/java/util/concurrent/CompletableFuture.java +++ b/src/share/classes/java/util/concurrent/CompletableFuture.java @@ -978,7 +978,15 @@ public class CompletableFuture implements Future, CompletionStage { } try { @SuppressWarnings("unchecked") T t = (T) r; - return f.apply(t).toCompletableFuture(); + CompletableFuture g = f.apply(t).toCompletableFuture(); + Object s = g.result; + if (s != null) + return new CompletableFuture(encodeRelay(s)); + CompletableFuture d = new CompletableFuture(); + UniRelay copy = new UniRelay(d, g); + g.push(copy); + copy.tryFire(SYNC); + return d; } catch (Throwable ex) { return new CompletableFuture(encodeThrowable(ex)); } diff --git a/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java b/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java new file mode 100644 index 000000000..ad77c863d --- /dev/null +++ b/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Consumer; + + +/** + * @test + * @bug 8068432 + * @run testng ThenComposeExceptionTest + * @summary Test that CompletableFuture.thenCompose works correctly if the + * composing future completes exceptionally + */ +@Test +public class ThenComposeExceptionTest { + + static final BiFunction, CompletableFuture, CompletableFuture> + THEN_COMPOSE = (f, fe) -> f.thenCompose(s -> fe); + + static final BiFunction, CompletableFuture, CompletableFuture> + THEN_COMPOSE_ASYNC = (f, fe) -> f.thenComposeAsync(s -> fe); + + static final Consumer> + COMPLETE_EXCEPTIONALLY = f -> f.completeExceptionally(new RuntimeException()); + + static final Consumer> + NOP = f -> { }; + + static Object[][] actionsDataProvider; + + @DataProvider(name = "actions") + static Object[][] actionsDataProvider() { + if (actionsDataProvider != null) { + return actionsDataProvider; + } + + List data = new ArrayList<>(); + data.add(new Object[]{"thenCompose and completeExceptionally", NOP, THEN_COMPOSE, COMPLETE_EXCEPTIONALLY}); + data.add(new Object[]{"thenComposeAsync and completeExceptionally", NOP, THEN_COMPOSE_ASYNC, COMPLETE_EXCEPTIONALLY}); + data.add(new Object[]{"completeExceptionally and thenCompose", COMPLETE_EXCEPTIONALLY, THEN_COMPOSE, NOP}); + data.add(new Object[]{"completeExceptionally and thenComposeAsync", COMPLETE_EXCEPTIONALLY, THEN_COMPOSE_ASYNC, NOP}); + + return actionsDataProvider = data.toArray(new Object[0][]); + } + + @Test(dataProvider = "actions") + public void testThenCompose( + String description, + Consumer> beforeAction, + BiFunction, CompletableFuture, CompletableFuture> composeFunction, + Consumer> afterAction) throws Exception { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture fe = new CompletableFuture<>(); + + // Ensure pre-composed stage is completed to trigger + // processing the composing future + f.complete(""); + + beforeAction.accept(fe); + + CompletableFuture f_thenCompose = composeFunction.apply(f, fe); + Assert.assertNotSame(f_thenCompose, fe, "Composed CompletableFuture returned directly"); + + AtomicReference eOnWhenComplete = new AtomicReference<>(); + f_thenCompose.whenComplete((r, e) -> eOnWhenComplete.set(e)); + + afterAction.accept(fe); + + Throwable eOnJoined = null; + try { + f_thenCompose.join(); + } + catch (Throwable t) { + eOnJoined = t; + } + + Assert.assertTrue(eOnWhenComplete.get() instanceof CompletionException, + "Incorrect exception reported on whenComplete"); + Assert.assertTrue(eOnJoined instanceof CompletionException, + "Incorrect exception reported when joined"); + } +} -- GitLab