Skip to content

Commit 448d92a

Browse files
committed
GROOVY-9381: Support async/await like ES7
1 parent 4e0dc1a commit 448d92a

File tree

22 files changed

+4136
-15
lines changed

22 files changed

+4136
-15
lines changed

src/antlr/GroovyLexer.g4

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,9 @@ BuiltInPrimitiveType
406406
;
407407

408408
ABSTRACT : 'abstract';
409+
ASYNC : 'async';
409410
ASSERT : 'assert';
411+
AWAIT : 'await';
410412

411413
fragment
412414
BOOLEAN : 'boolean';
@@ -485,6 +487,7 @@ VOLATILE : 'volatile';
485487
WHILE : 'while';
486488
YIELD : 'yield';
487489

490+
488491
// §3.10.1 Integer Literals
489492

490493
IntegerLiteral

src/antlr/GroovyParser.g4

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ modifier
130130
: classOrInterfaceModifier
131131
| m=( NATIVE
132132
| SYNCHRONIZED
133+
| ASYNC
133134
| TRANSIENT
134135
| VOLATILE
135136
| DEF
@@ -416,6 +417,8 @@ qualifiedNameElement
416417
| IN
417418
| AS
418419
| TRAIT
420+
| ASYNC
421+
| AWAIT
419422
;
420423

421424
qualifiedNameElements
@@ -491,8 +494,7 @@ closure
491494

492495
// GROOVY-8991: Difference in behaviour with closure and lambda
493496
closureOrLambdaExpression
494-
: closure
495-
| lambdaExpression
497+
: (ASYNC nls)? (closure | lambdaExpression)
496498
;
497499

498500
blockStatementsOpt
@@ -776,6 +778,9 @@ expression
776778
// must come before postfixExpression to resolve the ambiguities between casting and call on parentheses expression, e.g. (int)(1 / 2)
777779
: castParExpression castOperandExpression #castExprAlt
778780

781+
// await expression
782+
| AWAIT nls expression #awaitExprAlt
783+
779784
// qualified names, array expressions, method invocation, post inc/dec
780785
| postfixExpression #postfixExprAlt
781786

@@ -1243,7 +1248,9 @@ builtInType
12431248
keywords
12441249
: ABSTRACT
12451250
| AS
1251+
| ASYNC
12461252
| ASSERT
1253+
| AWAIT
12471254
| BREAK
12481255
| CASE
12491256
| CATCH
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package groovy.transform;
20+
21+
import org.codehaus.groovy.transform.GroovyASTTransformationClass;
22+
23+
import java.lang.annotation.Documented;
24+
import java.lang.annotation.ElementType;
25+
import java.lang.annotation.Retention;
26+
import java.lang.annotation.RetentionPolicy;
27+
import java.lang.annotation.Target;
28+
29+
/**
30+
* Annotation used to mark async methods, closures and lambda expressions.
31+
*
32+
* @since 6.0.0
33+
*/
34+
@Documented
35+
@Retention(RetentionPolicy.SOURCE)
36+
@Target({ElementType.METHOD, ElementType.LOCAL_VARIABLE, ElementType.FIELD})
37+
@GroovyASTTransformationClass({"org.codehaus.groovy.transform.AsyncASTTransformation"})
38+
public @interface Async {
39+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package groovy.util.concurrent.async;
20+
21+
import org.apache.groovy.util.SystemUtil;
22+
23+
import java.lang.invoke.MethodHandle;
24+
import java.lang.invoke.MethodHandles;
25+
import java.lang.invoke.MethodType;
26+
import java.util.concurrent.Executor;
27+
import java.util.concurrent.ExecutorService;
28+
import java.util.concurrent.Executors;
29+
import java.util.function.Supplier;
30+
31+
/**
32+
* Helper class for async/await operations
33+
*
34+
* @since 6.0.0
35+
*/
36+
public class AsyncHelper {
37+
private static final int PARALLELISM = SystemUtil.getIntegerSafe("groovy.async.parallelism", Runtime.getRuntime().availableProcessors() + 1);
38+
private static final Executor DEFAULT_EXECUTOR;
39+
private static int seq;
40+
41+
static {
42+
Executor tmpExecutor;
43+
try {
44+
MethodHandle mh = MethodHandles.lookup().findStatic(
45+
Executors.class,
46+
"newVirtualThreadPerTaskExecutor",
47+
MethodType.methodType(ExecutorService.class)
48+
);
49+
tmpExecutor = (Executor) mh.invoke();
50+
} catch (Throwable throwable) {
51+
// Fallback to default thread pool if virtual threads are not available
52+
tmpExecutor = Executors.newFixedThreadPool(PARALLELISM, r -> {
53+
Thread t = new Thread(r);
54+
t.setName("async-thread-" + seq++);
55+
return t;
56+
});
57+
}
58+
DEFAULT_EXECUTOR = tmpExecutor;
59+
}
60+
61+
/**
62+
* Submits a supplier for asynchronous execution using the default executor
63+
*
64+
* @param supplier the supplier
65+
* @param <T> the result type
66+
* @return the promise
67+
*/
68+
public static <T> Promise<T> async(Supplier<T> supplier) {
69+
return SimplePromise.of(supplier, DEFAULT_EXECUTOR);
70+
}
71+
72+
/**
73+
* Submits a supplier for asynchronous execution using the provided executor
74+
*
75+
* @param supplier the supplier
76+
* @param executor the executor
77+
* @param <T> the result type
78+
* @return the promise
79+
*/
80+
public static <T> Promise<T> async(Supplier<T> supplier, Executor executor) {
81+
return SimplePromise.of(supplier, executor);
82+
}
83+
84+
/**
85+
* Awaits the result of an awaitable
86+
*
87+
* @param awaitable the awaitable
88+
* @param <T> the result type
89+
* @return the result
90+
*/
91+
public static <T> T await(Awaitable<T> awaitable) {
92+
return awaitable.await();
93+
}
94+
95+
private AsyncHelper() {}
96+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package groovy.util.concurrent.async;
20+
21+
/**
22+
* Exception thrown when attempting to wait for the result of a promise
23+
* that aborted by throwing an exception. This exception can be
24+
* inspected using the {@link #getCause()} method.
25+
*
26+
* @see Promise
27+
* @since 6.0.0
28+
*/
29+
public class AwaitException extends RuntimeException {
30+
public AwaitException() {
31+
}
32+
33+
public AwaitException(String message) {
34+
super(message);
35+
}
36+
37+
public AwaitException(Throwable cause) {
38+
super(cause);
39+
}
40+
41+
public AwaitException(String message, Throwable cause) {
42+
super(message, cause);
43+
}
44+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package groovy.util.concurrent.async;
20+
21+
/**
22+
* Represents a result of an asynchronous computation
23+
*
24+
* @since 6.0.0
25+
*/
26+
public interface Awaitable<T> {
27+
/**
28+
* Waits if necessary for the computation to complete, and then retrieves its
29+
* result.
30+
*
31+
* @return the computed result
32+
* @throws AwaitException if the computation was cancelled or completed exceptionally
33+
*/
34+
T await();
35+
}

0 commit comments

Comments
 (0)