Skip to content

Commit 41b4812

Browse files
author
Ben Taylor
committed
8303742: CompletableFuture.orTimeout leaks if the future completes exceptionally
8304557: java/util/concurrent/CompletableFuture/CompletableFutureOrTimeoutExceptionallyTest.java times out Reviewed-by: phh Backport-of: ded6a81
1 parent cc70a50 commit 41b4812

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/java.base/share/classes/java/util/concurrent/CompletableFuture.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2891,7 +2891,7 @@ static final class Canceller implements BiConsumer<Object, Throwable> {
28912891
final Future<?> f;
28922892
Canceller(Future<?> f) { this.f = f; }
28932893
public void accept(Object ignore, Throwable ex) {
2894-
if (ex == null && f != null && !f.isDone())
2894+
if (f != null && !f.isDone())
28952895
f.cancel(false);
28962896
}
28972897
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8303742
27+
* @summary CompletableFuture.orTimeout can leak memory if completed exceptionally
28+
* @modules java.base/java.util.concurrent:open
29+
* @run junit/othervm -Xmx128m CompletableFutureOrTimeoutExceptionallyTest
30+
*/
31+
32+
import java.time.Duration;
33+
import java.util.concurrent.BlockingQueue;
34+
import java.util.concurrent.CompletableFuture;
35+
import java.util.concurrent.ScheduledThreadPoolExecutor;
36+
import java.util.concurrent.TimeUnit;
37+
38+
import org.junit.jupiter.api.Test;
39+
import static org.junit.jupiter.api.Assertions.assertTrue;
40+
41+
class CompletableFutureOrTimeoutExceptionallyTest {
42+
static final BlockingQueue<Runnable> delayerQueue;
43+
static {
44+
try {
45+
var delayerClass = Class.forName("java.util.concurrent.CompletableFuture$Delayer",
46+
true,
47+
CompletableFuture.class.getClassLoader());
48+
var delayerField = delayerClass.getDeclaredField("delayer");
49+
delayerField.setAccessible(true);
50+
delayerQueue = ((ScheduledThreadPoolExecutor)delayerField.get(null)).getQueue();
51+
} catch (Throwable t) {
52+
throw new ExceptionInInitializerError(t);
53+
}
54+
}
55+
56+
/**
57+
* Test that orTimeout task is cancelled if the CompletableFuture is completed Exceptionally
58+
*/
59+
@Test
60+
void testOrTimeoutWithCompleteExceptionallyDoesNotLeak() throws InterruptedException {
61+
assertTrue(delayerQueue.peek() == null);
62+
var future = new CompletableFuture<>().orTimeout(12, TimeUnit.HOURS);
63+
assertTrue(delayerQueue.peek() != null);
64+
future.completeExceptionally(new RuntimeException("This is fine"));
65+
while (delayerQueue.peek() != null) {
66+
Thread.sleep(100);
67+
};
68+
}
69+
70+
/**
71+
* Test that the completeOnTimeout task is cancelled if the CompletableFuture is completed Exceptionally
72+
*/
73+
@Test
74+
void testCompleteOnTimeoutWithCompleteExceptionallyDoesNotLeak() throws InterruptedException {
75+
assertTrue(delayerQueue.peek() == null);
76+
var future = new CompletableFuture<>().completeOnTimeout(null, 12, TimeUnit.HOURS);
77+
assertTrue(delayerQueue.peek() != null);
78+
future.completeExceptionally(new RuntimeException("This is fine"));
79+
while (delayerQueue.peek() != null) {
80+
Thread.sleep(100);
81+
};
82+
}
83+
}

0 commit comments

Comments
 (0)