Skip to content

Commit 7cd7f9e

Browse files
committed
Minor tweaks
1 parent 0b753ba commit 7cd7f9e

5 files changed

Lines changed: 200 additions & 180 deletions

File tree

src/main/java/groovy/concurrent/AwaitableAdapterRegistry.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,6 @@ private static <T> void completeFrom(CompletableFuture<T> cf, Future<T> future)
252252
cf.completeExceptionally(ce);
253253
} catch (ExecutionException e) {
254254
cf.completeExceptionally(e.getCause() != null ? e.getCause() : e);
255-
} catch (CancellationException e) {
256-
cf.completeExceptionally(e);
257255
} catch (Exception e) {
258256
cf.completeExceptionally(e);
259257
}

src/main/java/org/apache/groovy/runtime/async/AsyncSupport.java

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,7 @@ public static List<Object> awaitAll(Object... awaitables) {
456456
if (awaitables == null || awaitables.length == 0) {
457457
return new ArrayList<>();
458458
}
459-
for (int i = 0; i < awaitables.length; i++) {
460-
if (awaitables[i] == null) {
461-
throw new IllegalArgumentException("awaitAll: element at index " + i + " is null");
462-
}
463-
}
464-
CompletableFuture<?>[] futures =
465-
Arrays.stream(awaitables).map(AsyncSupport::toCompletableFuture).toArray(CompletableFuture[]::new);
459+
CompletableFuture<?>[] futures = toCompletableFutures(awaitables, "awaitAll");
466460
try {
467461
CompletableFuture.allOf(futures).join();
468462
} catch (CompletionException e) {
@@ -490,13 +484,7 @@ public static Object awaitAny(Object... awaitables) {
490484
if (awaitables == null || awaitables.length == 0) {
491485
throw new IllegalArgumentException("awaitAny requires at least one awaitable");
492486
}
493-
for (int i = 0; i < awaitables.length; i++) {
494-
if (awaitables[i] == null) {
495-
throw new IllegalArgumentException("awaitAny: element at index " + i + " is null");
496-
}
497-
}
498-
CompletableFuture<?>[] futures =
499-
Arrays.stream(awaitables).map(AsyncSupport::toCompletableFuture).toArray(CompletableFuture[]::new);
487+
CompletableFuture<?>[] futures = toCompletableFutures(awaitables, "awaitAny");
500488
try {
501489
return CompletableFuture.anyOf(futures).join();
502490
} catch (CompletionException e) {
@@ -521,13 +509,7 @@ public static List<AwaitResult<Object>> awaitAllSettled(Object... awaitables) {
521509
if (awaitables == null || awaitables.length == 0) {
522510
return new ArrayList<>();
523511
}
524-
for (int i = 0; i < awaitables.length; i++) {
525-
if (awaitables[i] == null) {
526-
throw new IllegalArgumentException("awaitAllSettled: element at index " + i + " is null");
527-
}
528-
}
529-
CompletableFuture<?>[] futures =
530-
Arrays.stream(awaitables).map(AsyncSupport::toCompletableFuture).toArray(CompletableFuture[]::new);
512+
CompletableFuture<?>[] futures = toCompletableFutures(awaitables, "awaitAllSettled");
531513
CompletableFuture.allOf(
532514
Arrays.stream(futures)
533515
.map(f -> f.handle((v, t) -> null))
@@ -545,6 +527,29 @@ private static CompletableFuture<?> toCompletableFuture(Object source) {
545527
return Awaitable.from(source).toCompletableFuture();
546528
}
547529

530+
/**
531+
* Validates that no element is {@code null} and converts all elements to
532+
* {@link CompletableFuture}s in one pass. Used by combinator methods
533+
* ({@code awaitAll}, {@code awaitAny}, {@code awaitAllSettled}, and their
534+
* non-blocking {@code Async} variants) to eliminate duplicate
535+
* null-checking and conversion loops.
536+
*
537+
* @param sources the source objects to validate and convert
538+
* @param callerName the method name for error messages
539+
* @return an array of completable futures corresponding to the sources
540+
* @throws IllegalArgumentException if any element is {@code null}
541+
*/
542+
private static CompletableFuture<?>[] toCompletableFutures(Object[] sources, String callerName) {
543+
CompletableFuture<?>[] futures = new CompletableFuture<?>[sources.length];
544+
for (int i = 0; i < sources.length; i++) {
545+
if (sources[i] == null) {
546+
throw new IllegalArgumentException(callerName + ": element at index " + i + " is null");
547+
}
548+
futures[i] = toCompletableFuture(sources[i]);
549+
}
550+
return futures;
551+
}
552+
548553
// ---- non-blocking combinators (return Awaitable) --------------------
549554

550555
/**
@@ -556,13 +561,7 @@ public static Awaitable<List<Object>> allAsync(Object... sources) {
556561
if (sources == null || sources.length == 0) {
557562
return Awaitable.of(new ArrayList<>());
558563
}
559-
CompletableFuture<?>[] futures = new CompletableFuture[sources.length];
560-
for (int i = 0; i < sources.length; i++) {
561-
if (sources[i] == null) {
562-
throw new IllegalArgumentException("Awaitable.all: element at index " + i + " is null");
563-
}
564-
futures[i] = toCompletableFuture(sources[i]);
565-
}
564+
CompletableFuture<?>[] futures = toCompletableFutures(sources, "Awaitable.all");
566565
CompletableFuture<List<Object>> combined = CompletableFuture.allOf(futures)
567566
.thenApply(v -> {
568567
List<Object> results = new ArrayList<>(futures.length);
@@ -584,12 +583,7 @@ public static <T> Awaitable<T> anyAsync(Object... sources) {
584583
if (sources == null || sources.length == 0) {
585584
throw new IllegalArgumentException("Awaitable.any requires at least one source");
586585
}
587-
for (int i = 0; i < sources.length; i++) {
588-
if (sources[i] == null) {
589-
throw new IllegalArgumentException("Awaitable.any: element at index " + i + " is null");
590-
}
591-
}
592-
CompletableFuture<?>[] futures = Arrays.stream(sources).map(AsyncSupport::toCompletableFuture).toArray(CompletableFuture[]::new);
586+
CompletableFuture<?>[] futures = toCompletableFutures(sources, "Awaitable.any");
593587
return GroovyPromise.of((CompletableFuture<T>) CompletableFuture.anyOf(futures));
594588
}
595589

@@ -602,12 +596,7 @@ public static Awaitable<List<AwaitResult<Object>>> allSettledAsync(Object... sou
602596
if (sources == null || sources.length == 0) {
603597
return Awaitable.of(new ArrayList<>());
604598
}
605-
for (int i = 0; i < sources.length; i++) {
606-
if (sources[i] == null) {
607-
throw new IllegalArgumentException("Awaitable.allSettled: element at index " + i + " is null");
608-
}
609-
}
610-
CompletableFuture<?>[] futures = Arrays.stream(sources).map(AsyncSupport::toCompletableFuture).toArray(CompletableFuture[]::new);
599+
CompletableFuture<?>[] futures = toCompletableFutures(sources, "Awaitable.allSettled");
611600
// Wait for all to settle (handle converts failures to non-exceptional completions)
612601
CompletableFuture<List<AwaitResult<Object>>> combined = CompletableFuture.allOf(
613602
Arrays.stream(futures)

src/main/java/org/codehaus/groovy/transform/AsyncTransformHelper.java

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -260,25 +260,7 @@ public static Expression buildGetExecutorCall() {
260260
* @return {@code true} if at least one {@code yieldReturn} call is found
261261
*/
262262
public static boolean containsYieldReturn(Statement code) {
263-
boolean[] found = {false};
264-
code.visit(new CodeVisitorSupport() {
265-
@Override
266-
public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
267-
if (YIELD_RETURN_METHOD.equals(call.getMethod())
268-
&& ASYNC_SUPPORT_CLASS.equals(call.getOwnerType().getName())) {
269-
found[0] = true;
270-
}
271-
if (!found[0]) {
272-
super.visitStaticMethodCallExpression(call);
273-
}
274-
}
275-
276-
@Override
277-
public void visitClosureExpression(ClosureExpression expression) {
278-
// Do not descend into nested closures — each manages its own generator
279-
}
280-
});
281-
return found[0];
263+
return containsStaticCall(code, YIELD_RETURN_METHOD);
282264
}
283265

284266
// ---- rewriting utilities --------------------------------------------
@@ -353,11 +335,25 @@ public static Parameter createGenParam() {
353335
* @return {@code true} if at least one {@code defer} call is found
354336
*/
355337
public static boolean containsDefer(Statement code) {
338+
return containsStaticCall(code, DEFER_METHOD);
339+
}
340+
341+
/**
342+
* Scans the given statement tree for a static method call to
343+
* {@code AsyncSupport.<methodName>()}. Does <em>not</em> descend
344+
* into nested {@link ClosureExpression}s, since each nested closure
345+
* manages its own transformation independently.
346+
*
347+
* @param code the statement tree to scan
348+
* @param methodName the method name to look for on {@code AsyncSupport}
349+
* @return {@code true} if at least one matching call is found
350+
*/
351+
private static boolean containsStaticCall(Statement code, String methodName) {
356352
boolean[] found = {false};
357353
code.visit(new CodeVisitorSupport() {
358354
@Override
359355
public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
360-
if (DEFER_METHOD.equals(call.getMethod())
356+
if (methodName.equals(call.getMethod())
361357
&& ASYNC_SUPPORT_CLASS.equals(call.getOwnerType().getName())) {
362358
found[0] = true;
363359
}

0 commit comments

Comments
 (0)