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 79ca16ae..fea4462a 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -208,11 +208,12 @@ public static CompletableFuture> mostResultsOfSuccess( if (cfs.length == 0) return CompletableFuture.completedFuture(arrayList()); if (cfs.length == 1) { - final CompletableFuture f = copy(toCf(cfs[0])); + final CompletableFuture f = copy(toNonMinCf(cfs[0])); return orTimeout(f, timeout, unit).handle((unused, ex) -> arrayList(getSuccessNow(f, valueIfNotSuccess))); } - final CompletableFuture[] cfArray = f_toCfArray(cfs); + // MUST be Non-Minimal CF instances in order to read results, otherwise UnsupportedOperationException + final CompletableFuture[] cfArray = f_toNonMinCfArray(cfs); return orTimeout(CompletableFuture.allOf(cfArray), timeout, unit) .handle((unused, ex) -> MGetSuccessNow(valueIfNotSuccess, cfArray)); } @@ -279,17 +280,31 @@ private static CompletableFuture f_cast(CompletableFuture f) { * IGNORE the compile-time type check. */ private static CompletableFuture[] f_toCfArray(CompletionStage[] stages) { + return toCfArray0(stages, CompletableFutureUtils::toCf); + } + + /** + * Force converts {@link CompletionStage} array to {@link CompletableFuture} array, + * IGNORE the compile-time type check. + */ + private static CompletableFuture[] f_toNonMinCfArray(CompletionStage[] stages) { + return toCfArray0(stages, CompletableFutureUtils::toNonMinCf); + } + + private static CompletableFuture[] toCfArray0( + CompletionStage[] stages, + Function, CompletableFuture> converter) { requireNonNull(stages, "cfs is null"); @SuppressWarnings("unchecked") CompletableFuture[] ret = new CompletableFuture[stages.length]; for (int i = 0; i < stages.length; i++) { - ret[i] = toCf(requireNonNull(stages[i], "cf" + (i + 1) + " is null")); + ret[i] = converter.apply(requireNonNull(stages[i], "cf" + (i + 1) + " is null")); } return ret; } /** - * Converts CompletionStage to CompletableFuture, reuse cf instance as much as possible. + * Converts CompletionStage to CompletableFuture, reuse cf instance as many as possible. *

* CAUTION: because reused the CF instances, * so the returned CF instances MUST NOT be written(e.g. {@link CompletableFuture#complete(Object)}). @@ -302,6 +317,24 @@ private static CompletableFuture toCf(CompletionStage s) { else return (CompletableFuture) s.toCompletableFuture(); } + /** + * Converts CompletionStage to Non-MinimalStage CompletableFuture, reuse cf instance as many as possible. + *

+ * CAUTION: because reused the CF instances, + * so the returned CF instances MUST NOT be written(e.g. {@link CompletableFuture#complete(Object)}). + * Otherwise, the caller should defensive copy instead of writing it directly. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private static CompletableFuture toNonMinCf(CompletionStage s) { + final CompletableFuture f; + if (s instanceof CompletableFuture) f = (CompletableFuture) s; + else if (s instanceof Cffu) f = ((Cffu) s).cffuUnwrap(); + else return (CompletableFuture) s.toCompletableFuture(); + + return "java.util.concurrent.CompletableFuture$MinimalStage".equals(s.getClass().getName()) + ? f.toCompletableFuture() : f; + } + //////////////////////////////////////////////////////////////////////////////// //# anyOf* methods ////////////////////////////////////////////////////////////////////////////////