Skip to content

GROOVY-9381: Add native async/await support#2387

Open
daniellansun wants to merge 1 commit intomasterfrom
GROOVY-9381_3
Open

GROOVY-9381: Add native async/await support#2387
daniellansun wants to merge 1 commit intomasterfrom
GROOVY-9381_3

Conversation

@daniellansun
Copy link
Contributor

@daniellansun daniellansun commented Mar 1, 2026

@codecov-commenter
Copy link

codecov-commenter commented Mar 1, 2026

Codecov Report

❌ Patch coverage is 89.16563% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.9030%. Comparing base (a2a7da6) to head (5df6414).

Files with missing lines Patch % Lines
.../org/apache/groovy/runtime/async/AsyncSupport.java 88.3895% 17 Missing and 14 partials ⚠️
...va/groovy/concurrent/AwaitableAdapterRegistry.java 84.8921% 7 Missing and 14 partials ⚠️
...ehaus/groovy/transform/AsyncASTTransformation.java 82.2917% 6 Missing and 11 partials ⚠️
...che/groovy/runtime/async/AsyncStreamGenerator.java 88.3721% 3 Missing and 7 partials ⚠️
...odehaus/groovy/transform/AsyncTransformHelper.java 93.9394% 0 Missing and 4 partials ⚠️
...va/org/apache/groovy/parser/antlr4/AstBuilder.java 95.8333% 3 Missing ⚠️
...org/apache/groovy/runtime/async/GroovyPromise.java 96.2963% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2387        +/-   ##
==================================================
+ Coverage     66.7519%   66.9030%   +0.1511%     
- Complexity      29867      30138       +271     
==================================================
  Files            1382       1392        +10     
  Lines          116106     116908       +802     
  Branches        20472      20607       +135     
==================================================
+ Hits            77503      78215       +712     
- Misses          32272      32312        +40     
- Partials         6331       6381        +50     
Files with missing lines Coverage Δ
src/main/java/groovy/concurrent/AsyncStream.java 100.0000% <100.0000%> (ø)
src/main/java/groovy/concurrent/AwaitResult.java 100.0000% <100.0000%> (ø)
src/main/java/groovy/concurrent/Awaitable.java 100.0000% <100.0000%> (ø)
.../main/java/groovy/concurrent/AwaitableAdapter.java 100.0000% <100.0000%> (ø)
...g/apache/groovy/parser/antlr4/ModifierManager.java 93.4210% <100.0000%> (ø)
...ain/java/org/codehaus/groovy/ast/ModifierNode.java 77.7778% <100.0000%> (+0.4193%) ⬆️
...org/apache/groovy/runtime/async/GroovyPromise.java 96.2963% <96.2963%> (ø)
...va/org/apache/groovy/parser/antlr4/AstBuilder.java 86.8007% <95.8333%> (+0.2935%) ⬆️
...odehaus/groovy/transform/AsyncTransformHelper.java 93.9394% <93.9394%> (ø)
...che/groovy/runtime/async/AsyncStreamGenerator.java 88.3721% <88.3721%> (ø)
... and 3 more

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements GROOVY-9381 by adding native async/await syntax to the Groovy language (including for await, yield return async generators, and defer), plus runtime support and extensive documentation/tests.

Changes:

  • Extend the Groovy grammar + AST builder to parse/compile async, await, for await, yield return, and defer.
  • Add/extend runtime primitives (Awaitable, AsyncStream, adapters/registry, promise + generator implementation) and @Async AST transformation support.
  • Add comprehensive tests and new spec documentation; add Reactor/RxJava test dependencies for adapter interop coverage.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
versions.properties Adds version pins for Reactor and RxJava3 used by new integration tests.
build.gradle Adds Reactor/RxJava3 as testImplementation dependencies.
gradle/verification-metadata.xml Adds verification metadata entries for new test dependencies.
src/antlr/GroovyLexer.g4 Introduces lexer tokens for async, await, defer.
src/antlr/GroovyParser.g4 Adds grammar support for async closure/lambda, await expressions, for await, yield return, and defer.
src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java Lowers new syntax into runtime calls (e.g., AsyncSupport.await, stream conversion/cleanup, defer, yieldReturn).
src/main/java/org/codehaus/groovy/ast/ModifierNode.java Treats ASYNC as a non-bytecode modifier (opcode 0).
src/main/java/org/codehaus/groovy/transform/AsyncTransformHelper.java Centralizes AST construction + scanning/rewriting for async constructs.
src/main/java/org/codehaus/groovy/transform/AsyncASTTransformation.java Implements @Async transformation using shared helper logic (await rewrite, generator/defer handling).
src/main/java/groovy/transform/Async.java Adds @Async annotation and documentation.
src/main/java/groovy/concurrent/* Adds new async public API (Awaitable, AsyncStream, adapters/registry, AwaitResult).
src/main/java/org/apache/groovy/runtime/async/* Adds runtime implementations (GroovyPromise, AsyncStreamGenerator) used by compiler output.
src/spec/doc/core-async-await.adoc Adds user-facing language documentation/spec for async/await.
src/test/groovy/org/codehaus/groovy/transform/* Adds extensive test coverage for async/await, generators, defer, adapters, and virtual threads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

| VOLATILE
| DEF
| VAR
| ASYNC
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding ASYNC to the general modifier rule means async can now appear anywhere modifiers are allowed (e.g., on fields, variables, classes) and will likely be silently ignored (since it maps to opcode 0). If async is intended to be a method-only modifier, consider restricting it in the grammar or adding a parse/semantic error when async is used in unsupported contexts, to avoid misleading code compiling without effect.

Suggested change
| ASYNC

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +158
def customPool = Executors.newFixedThreadPool(2, { r ->
def t = new Thread(r)
t.setName("custom-async-" + t.getId())
t
})
Awaitable.setExecutor(customPool)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom fixed thread pool created here is never shut down. Since the thread factory creates non-daemon threads, this can leak threads and potentially hang the test JVM. Please ensure the executor is shut down (preferably in the existing finally block) after restoring the previous executor.

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +190
Awaitable.setExecutor(Executors.newSingleThreadExecutor())
assert Awaitable.getExecutor() != originalExecutor
// Reset to null — should restore default
Awaitable.setExecutor(null)
def restored = Awaitable.getExecutor()
assert restored != null
// Verify it works
def task = async { 42 }; def awaitable = task()
assert await(awaitable) == 42
// Restore original
Awaitable.setExecutor(originalExecutor)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test sets the global executor to a newSingleThreadExecutor but never shuts that executor down. Even though the executor is reset to null later, the underlying thread can remain alive and leak across the suite. Please shut down the created executor (e.g., keep a reference and shutdown in a finally block).

Suggested change
Awaitable.setExecutor(Executors.newSingleThreadExecutor())
assert Awaitable.getExecutor() != originalExecutor
// Reset to null — should restore default
Awaitable.setExecutor(null)
def restored = Awaitable.getExecutor()
assert restored != null
// Verify it works
def task = async { 42 }; def awaitable = task()
assert await(awaitable) == 42
// Restore original
Awaitable.setExecutor(originalExecutor)
def customExecutor = Executors.newSingleThreadExecutor()
try {
Awaitable.setExecutor(customExecutor)
assert Awaitable.getExecutor() != originalExecutor
// Reset to null — should restore default
Awaitable.setExecutor(null)
def restored = Awaitable.getExecutor()
assert restored != null
// Verify it works
def task = async { 42 }; def awaitable = task()
assert await(awaitable) == 42
} finally {
// Restore original and shut down custom executor to avoid thread leaks
Awaitable.setExecutor(originalExecutor)
customExecutor.shutdown()
}

Copilot uses AI. Check for mistakes.
Comment on lines +202 to +207
static Executor myPool = Executors.newFixedThreadPool(1, { r ->
def t = new Thread(r)
t.setName("my-pool-thread")
t
})

Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static Executor created via Executors.newFixedThreadPool uses non-daemon threads and is never shut down. This can leak threads for the remainder of the test run and may prevent the JVM from exiting. Consider using daemon threads (as other tests do) and/or explicitly shutting down the pool at the end of the script.

Copilot uses AI. Check for mistakes.
@asf-gitbox-commits asf-gitbox-commits force-pushed the GROOVY-9381_3 branch 2 times, most recently from c3808d6 to 1afcb33 Compare March 8, 2026 05:25
@asf-gitbox-commits asf-gitbox-commits force-pushed the GROOVY-9381_3 branch 5 times, most recently from 7dde6e5 to 9124f63 Compare March 10, 2026 16:53
@daniellansun daniellansun changed the title GROOVY-9381: Support async/await like ES7 GROOVY-9381: Add native async/await support Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants