Skip to content

Commit a240fc8

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

9 files changed

Lines changed: 3343 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: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
MethodHandles.Lookup lookup = MethodHandles.lookup();
45+
MethodHandle mh = lookup.findStatic(
46+
Executors.class,
47+
"newVirtualThreadPerTaskExecutor",
48+
MethodType.methodType(ExecutorService.class)
49+
);
50+
tmpExecutor = (Executor) mh.invoke();
51+
} catch (Throwable throwable) {
52+
// Fallback to default thread pool if virtual threads are not available
53+
tmpExecutor = Executors.newFixedThreadPool(PARALLELISM, r -> {
54+
Thread t = new Thread(r);
55+
t.setName("async-thread-" + seq++);
56+
return t;
57+
});
58+
}
59+
DEFAULT_EXECUTOR = tmpExecutor;
60+
}
61+
62+
/**
63+
* Submits a supplier for asynchronous execution using the default executor
64+
*
65+
* @param supplier the supplier
66+
* @param <T> the result type
67+
* @return the promise
68+
*/
69+
public static <T> Promise<T> async(Supplier<T> supplier) {
70+
return SimplePromise.of(supplier, DEFAULT_EXECUTOR);
71+
}
72+
73+
/**
74+
* Submits a supplier for asynchronous execution using the provided executor
75+
*
76+
* @param supplier the supplier
77+
* @param executor the executor
78+
* @param <T> the result type
79+
* @return the promise
80+
*/
81+
public static <T> Promise<T> async(Supplier<T> supplier, Executor executor) {
82+
return SimplePromise.of(supplier, executor);
83+
}
84+
85+
/**
86+
* Awaits the result of an awaitable
87+
*
88+
* @param awaitable the awaitable
89+
* @param <T> the result type
90+
* @return the result
91+
*/
92+
public static <T> T await(Awaitable<T> awaitable) {
93+
return awaitable.await();
94+
}
95+
96+
private AsyncHelper() {}
97+
}
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)