Skip to content

Commit b7853ee

Browse files
committed
Merge branch 'improve GC of mostSuccessOf* methods' into dev-orl
2 parents e675eb8 + aee7923 commit b7853ee

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -717,9 +717,11 @@ private static <T> CompletableFuture<T> f_mostSuccessTupleWithEhOf0(
717717

718718
private static <T> CompletableFuture<T> f_mostSuccessTupleOf0(
719719
Executor executorWhenTimeout, long timeout, TimeUnit unit, CompletionStage<?>[] stages) {
720-
// MUST be *Non-Minimal* CF instances in order to read results(`getSuccessNow`),
721-
// otherwise UnsupportedOperationException
722-
final CompletableFuture<Object>[] cfArray = toNonMinCfArray0(stages);
720+
// 1. MUST be non-minimal-stage CF instances in order to read results(`getSuccessNow`), otherwise UnsupportedOpException.
721+
// 2. SHOULD copy input cfs(by calling `exceptionally` method) to avoid memory leaks,
722+
// otherwise all input cfs would be retained until output cf completes.
723+
CompletableFuture<?>[] cfArray = mapArray(stages, CompletableFuture[]::new,
724+
s -> toNonMinCf0(s).exceptionally(v -> null));
723725
return cffuCompleteOnTimeout(CompletableFuture.allOf(cfArray), null, timeout, unit, executorWhenTimeout)
724726
.handle((unused, ex) -> f_tupleOf0(f_mGetSuccessNow0(null, cfArray)));
725727
}
@@ -944,9 +946,11 @@ private static <T> CompletableFuture<List<T>> mostSuccessResultsOf0(
944946
.handle((unused, ex) -> arrayList(getSuccessNow(f, valueIfNotSuccess)));
945947
}
946948

947-
// MUST be non-minimal-stage CF instances in order to read results(`getSuccessNow`),
948-
// otherwise UnsupportedOperationException
949-
final CompletableFuture<T>[] cfArray = toNonMinCfArray0(cfs);
949+
// 1. MUST be non-minimal-stage CF instances in order to read results(`getSuccessNow`), otherwise UnsupportedOpException.
950+
// 2. SHOULD copy input cfs(by calling `exceptionally` method) to avoid memory leaks,
951+
// otherwise all input cfs would be retained until output cf completes.
952+
CompletableFuture<T>[] cfArray = mapArray(cfs, CompletableFuture[]::new,
953+
s -> LLCF.<T>toNonMinCf0(s).exceptionally(v -> valueIfNotSuccess));
950954
return cffuCompleteOnTimeout(CompletableFuture.allOf(cfArray), null, timeout, unit, executorWhenTimeout)
951955
.handle((unused, ex) -> arrayList(f_mGetSuccessNow0(valueIfNotSuccess, cfArray)));
952956
}

cffu-core/src/test/java/io/foldright/cffu/LLCFTest.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,46 @@ class LLCFTest : FunSpec({
2525
LLCF.f_toCf0(FooCs(incomplete)) shouldBeSameInstanceAs incomplete
2626
}
2727

28+
test("toNonMinCfArray0") {
29+
val minStages = arrayOf(
30+
completedStage(n),
31+
testCffuFac.completedStage(n),
32+
)
33+
LLCF.toNonMinCfArray0(minStages).forEachIndexed { idx, f ->
34+
if (isJava9Plus()) {
35+
f.shouldNotBeSameInstanceAs(minStages[idx])
36+
} else {
37+
if (idx == 0)
38+
f.shouldBeSameInstanceAs(minStages[idx])
39+
else {
40+
f.shouldNotBeSameInstanceAs(minStages[idx])
41+
f.shouldBeSameInstanceAs((minStages[idx] as Cffu<Int>).cffuUnwrap())
42+
}
43+
}
44+
f.shouldBeCompleted()
45+
f.join() shouldBe n
46+
LLCF.isMinStageCf(f).shouldBeFalse()
47+
f.shouldNotBeMinimalStage()
48+
}
49+
50+
val cfs = arrayOf<CompletionStage<Int>>(
51+
completedFuture(n),
52+
testCffuFac.completedFuture(n),
53+
)
54+
LLCF.toNonMinCfArray0(cfs).forEachIndexed { idx, f ->
55+
if (idx == 0)
56+
f.shouldBeSameInstanceAs(cfs[idx])
57+
else {
58+
f.shouldNotBeSameInstanceAs(cfs[idx])
59+
f.shouldBeSameInstanceAs((cfs[idx] as Cffu<Int>).cffuUnwrap())
60+
}
61+
f.shouldBeCompleted()
62+
f.join() shouldBe n
63+
LLCF.isMinStageCf(f).shouldBeFalse()
64+
f.shouldNotBeMinimalStage()
65+
}
66+
}
67+
2868
test("f_toCfCopy0") {
2969
arrayOf(
3070
completedStage(n),

0 commit comments

Comments
 (0)