Skip to content

Commit 11e51ee

Browse files
committed
GROOVY-9381: Support async/await like ES7(backend)
1 parent 4e0dc1a commit 11e51ee

9 files changed

Lines changed: 3341 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 groovy.transform.stc.POJO
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+
@POJO
35+
@CompileStatic
36+
@Retention(RetentionPolicy.SOURCE)
37+
@Target([ElementType.METHOD, ElementType.LOCAL_VARIABLE, ElementType.FIELD])
38+
@Documented
39+
@interface Async {
40+
}
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
33+
*/
34+
T await();
35+
}

0 commit comments

Comments
 (0)