From 25614c6c8a4b5ab4a12a4552774667fc16d5b47f Mon Sep 17 00:00:00 2001 From: EricLin Date: Sat, 17 Aug 2024 09:26:05 +0800 Subject: [PATCH] fix: merge the JDK bug fix "`CompletableFuture.orTimeout` leaks if the future completes exceptionally", more info see - the JDK bug issue: https://bugs.openjdk.org/browse/JDK-8303742 - PR review: https://github.com/openjdk/jdk/pull/13059 - JDK bugfix commit: https://github.com/openjdk/jdk/commit/ded6a8131970ac2f7ae59716769e6f6bae3b809a --- .../java/io/foldright/cffu/CompletableFutureUtils.java | 7 ++++++- .../main/java/io/foldright/cffu/DelayExecutionHelpers.java | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) 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 45707cf9..337e1505 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -3725,6 +3725,11 @@ public static > C cffuOrTimeout( * Unless all subsequent actions of dependent CompletableFutures is ensured executing async * (aka. the dependent CompletableFutures is created by async methods), using this method and {@link CompletableFuture#orTimeout(long, TimeUnit)} * is one less thread switch of task execution when triggered by timeout. + *

+ * Note: Before Java 21(Java 20-), {@link CompletableFuture#orTimeout(long, TimeUnit)} + * can leak if the future completes exceptionally, more info see + * JDK-8303742 + * and JDK bugfix commit. * * @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 @@ -3741,7 +3746,7 @@ public static > C orTimeout(C cfThis, long timeou } else { // below code is copied from CompletableFuture#orTimeout with small adoption if (!cfThis.isDone()) { - ScheduledFuture f = Delayer.delayToTimoutCf(cfThis, timeout, unit); + ScheduledFuture f = Delayer.delayToTimeoutCf(cfThis, timeout, unit); cfThis.whenComplete(new FutureCanceller(f)); } } diff --git a/cffu-core/src/main/java/io/foldright/cffu/DelayExecutionHelpers.java b/cffu-core/src/main/java/io/foldright/cffu/DelayExecutionHelpers.java index 43552b89..8f4325ff 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/DelayExecutionHelpers.java +++ b/cffu-core/src/main/java/io/foldright/cffu/DelayExecutionHelpers.java @@ -36,7 +36,7 @@ static ScheduledFuture delay(Runnable command, long delay, TimeUnit unit) { * @return a Future can be used to cancel the delayed task(timeout CF) * @see FutureCanceller */ - static ScheduledFuture delayToTimoutCf(CompletableFuture cf, long delay, TimeUnit unit) { + static ScheduledFuture delayToTimeoutCf(CompletableFuture cf, long delay, TimeUnit unit) { return delay(new CfTimeout(cf), delay, unit); } @@ -188,7 +188,7 @@ public void run() { * code is copied from {@link CompletableFuture.Canceller} with small adoption. * * @see Delayer#delay(Runnable, long, TimeUnit) - * @see Delayer#delayToTimoutCf(CompletableFuture, long, TimeUnit) + * @see Delayer#delayToTimeoutCf(CompletableFuture, long, TimeUnit) * @see Delayer#delayToCompleteCf(CompletableFuture, Object, long, TimeUnit) */ @SuppressWarnings("JavadocReference") @@ -202,7 +202,7 @@ final class FutureCanceller implements BiConsumer { @Override public void accept(Object ignore, @Nullable Throwable ex) { - if (ex == null && f != null && !f.isDone()) + if (f != null && !f.isDone()) f.cancel(false); } }