From 5375f54353e27f7c00f0d8002915d13d8a5f4084 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Sun, 2 Jun 2024 19:48:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20safe=20`orTimeout*`=20methods=20keep=20?= =?UTF-8?q?the=20direct=20`TimeoutException`=20instead=20of=20`CompletionE?= =?UTF-8?q?xception`=20wrapper=20=E2=8F=B3=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit consistent with `Cf.orTimeout` builtin method ✨ --- .../src/main/java/io/foldright/cffu/Cffu.java | 10 ------- .../cffu/CompletableFutureUtils.java | 29 +++++++------------ .../cffu/CompletableFutureUtilsTest.java | 8 ++--- .../kotlin/CompletableFutureExtensions.kt | 6 ---- 4 files changed, 13 insertions(+), 40 deletions(-) diff --git a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java index ef94e3d0c..3f8637c67 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java +++ b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java @@ -946,11 +946,6 @@ public Cffu exceptionallyAsync(Function fn, Executor * if not otherwise completed before the given timeout. *

* Uses {@link #defaultExecutor()} as {@code executorWhenTimeout}. - *

- * CAUTION:
- * If the wait timed out, the returned Cffu complete exceptionally with a CompletionException holding - * the {@link TimeoutException} as its cause; NOT a direct {@link TimeoutException} - * like {@link #unsafeOrTimeout(long, TimeUnit)}/{@link CompletableFuture#orTimeout(long, TimeUnit)}. * * @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of {@code unit} * @param unit a {@code TimeUnit} determining how to interpret the {@code timeout} parameter @@ -964,11 +959,6 @@ public Cffu orTimeout(long timeout, TimeUnit unit) { /** * Exceptionally completes this Cffu with a {@link TimeoutException} * if not otherwise completed before the given timeout. - *

- * CAUTION:
- * If the wait timed out, the returned Cffu complete exceptionally with a CompletionException holding - * the {@link TimeoutException} as its cause; NOT a direct {@link TimeoutException} - * like {@link #unsafeOrTimeout(long, TimeUnit)}/{@link CompletableFuture#orTimeout(long, TimeUnit)}. * * @param executorWhenTimeout the async executor when triggered by timeout * @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of {@code unit} diff --git a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java index e98ed3293..ae22c2e69 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -1487,11 +1487,6 @@ C exceptionallyAsync(C cf, Function fn, Executor executo * if not otherwise completed before the given timeout. *

* Uses CompletableFuture's default asynchronous execution facility as {@code executorWhenTimeout}. - *

- * CAUTION:
- * If the wait timed out, the returned CompletableFuture complete exceptionally with a CompletionException holding - * the {@link TimeoutException} as its cause; NOT a direct {@link TimeoutException} - * like {@link #orTimeout(CompletableFuture, long, TimeUnit)}/{@link CompletableFuture#orTimeout(long, TimeUnit)}. * * @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of {@code unit} * @param unit a {@code TimeUnit} determining how to interpret the {@code timeout} parameter @@ -1505,11 +1500,6 @@ public static > C cffuOrTimeout(C cf, long timeou /** * Exceptionally completes given CompletableFuture with a {@link TimeoutException} * if not otherwise completed before the given timeout. - *

- * CAUTION:
- * If the wait timed out, the returned CompletableFuture complete exceptionally with a CompletionException holding - * the {@link TimeoutException} as its cause; NOT a direct {@link TimeoutException} - * like {@link #orTimeout(CompletableFuture, long, TimeUnit)}/{@link CompletableFuture#orTimeout(long, TimeUnit)}. * * @param executorWhenTimeout the async executor when triggered by timeout * @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of {@code unit} @@ -1646,17 +1636,20 @@ C completeOnTimeout(C cf, @Nullable T value, long timeout, TimeUnit unit) { @SuppressWarnings("unchecked") private static > C hopExecutorIfAtCfDelayerThread(C cf, Executor asyncExecutor) { - final CompletionStage f = (CompletionStage) cf; + CompletableFuture ret = newIncompleteFuture(cf); - return (C) f.handle((v, ex) -> null).thenCompose(unused -> { - if (!atCfDelayerThread()) return f; + cf.whenComplete((v, ex) -> { + if (!atCfDelayerThread()) completeCf(ret, v, ex); + else delayedExecutor(0, TimeUnit.SECONDS, asyncExecutor) + .execute(() -> completeCf(ret, v, ex)); + }); - CompletableFuture signal = new CompletableFuture<>(); - delayedExecutor(0, TimeUnit.SECONDS, asyncExecutor) - .execute(() -> signal.complete(null)); + return (C) ret; + } - return signal.thenCompose(v -> f); - }); + private static void completeCf(CompletableFuture cf, Object value, @Nullable Throwable ex) { + if (ex == null) cf.complete(value); + else cf.completeExceptionally(ex); } //# Advanced methods of CompletionStage diff --git a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java index f5261b345..5b0d850d0 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java @@ -1043,17 +1043,13 @@ void test_safeBehavior_orTimeout() { }).join()); assertEquals(n, cffuOrTimeout(createIncompleteFuture(), 5, TimeUnit.MILLISECONDS).handle((r, ex) -> { - // FIXME: Not TimeoutException, not compatible with orTimeout method - assertInstanceOf(CompletionException.class, ex); - assertInstanceOf(TimeoutException.class, ex.getCause()); + assertInstanceOf(TimeoutException.class, ex); assertFalse(Delayer.atCfDelayerThread()); assertNotSame(testThread, currentThread()); return n; }).join()); assertEquals(n, cffuOrTimeout(createIncompleteFuture(), executorService, 5, TimeUnit.MILLISECONDS).handle((r, ex) -> { - // FIXME: Not TimeoutException, not compatible with orTimeout method - assertInstanceOf(CompletionException.class, ex); - assertInstanceOf(TimeoutException.class, ex.getCause()); + assertInstanceOf(TimeoutException.class, ex); assertFalse(Delayer.atCfDelayerThread()); assertTrue(TestThreadPoolManager.isRunInExecutor(executorService)); return n; diff --git a/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt b/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt index cafaad70a..8db439e19 100644 --- a/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt +++ b/cffu-kotlin/src/main/java/io/foldright/cffu/kotlin/CompletableFutureExtensions.kt @@ -771,9 +771,6 @@ fun > C.exceptionallyAsync(fn: Function> C.cffuOrTimeout(timeout: Long, unit: TimeU * Exceptionally completes this CompletableFuture with a TimeoutException * if not otherwise completed before the given timeout. * - * **CAUTION:** If the wait timed out, the returned CompletableFuture complete exceptionally with a CompletionException - * holding the [TimeoutException] as its cause; NOT a direct [TimeoutException] like [orTimeout]/[CompletableFuture.orTimeout]. - * * @param executorWhenTimeout the async executor when triggered by timeout * @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of `unit` * @param unit a `TimeUnit` determining how to interpret the `timeout` parameter