You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Daniel Sun (Jira)" <ji...@apache.org> on 2026/03/10 17:39:00 UTC

[jira] [Updated] (GROOVY-9381) Support async/await like ES7

     [ https://issues.apache.org/jira/browse/GROOVY-9381?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Daniel Sun updated GROOVY-9381:
-------------------------------
    Description: 
h1. Add native async/await support
h2. Summary

Introduce first-class {{{}async{}}}/{{{}await{}}} language support to Groovy, enabling developers to write asynchronous code in a sequential, readable style — on par with the async/await facilities in JavaScript (ES2017), C# (5.0), Kotlin (coroutines), and Swift (5.5).
h2. Motivation

Modern JVM applications are overwhelmingly concurrent. Web services, data pipelines, and reactive systems spend most of their time waiting for network I/O, database queries, or downstream services. The JVM offers powerful but low-level concurrency primitives ({{{}CompletableFuture{}}}, {{{}Flow.Publisher{}}}, {{{}ExecutorService{}}}), and while libraries like RxJava and Project Reactor raise the abstraction level, they introduce their own learning curve and cannot alter the language's control-flow syntax.

Today, a typical three-step async workflow in Groovy looks like this:
{code:groovy}
CompletableFuture.supplyAsync { fetchUserId() }
    .thenCompose { id -> CompletableFuture.supplyAsync { lookupName(id) } }
    .thenCompose { name -> CompletableFuture.supplyAsync { loadProfile(name) } }
    .exceptionally { ex -> handleError(ex) }
{code}
The business logic is obscured by plumbing. Exception handling is decoupled from the code that raises exceptions, and the control flow reads inside-out.

With the proposed {{{}async{}}}/{{{}await{}}}, the same logic becomes:
{code:groovy}
async fetchProfile() {
    def id   = await fetchUserIdAsync()
    def name = await lookupNameAsync(id)
    return await loadProfileAsync(name)
}
{code}
This reads identically to synchronous code. Standard {{{}try{}}}/{{{}catch{}}}, {{{}for{}}}, {{{}if{}}}, and variable assignment all compose naturally — no callbacks, no chained lambdas.
h2. Scope

This proposal introduces the following language constructs and runtime APIs:
h3. Language Constructs
||Construct||Syntax||Description||
|Async method|async ReturnType methodName(params) \{ ... }|Declares a method that executes asynchronously and returns {{Awaitable<T>}}|
|Await expression|{{await expr}} / {{await(expr)}}|Suspends the enclosing async context until the awaited computation completes; transparently unwraps the result|
|Async closure / lambda|async \{ params -> body } / async (params) -> \{ body }|Creates a reusable asynchronous function (must be explicitly invoked to start execution)|
|For-await loop|for await (item in source) \{ ... }|Iterates over an {{{}AsyncStream{}}}, {{{}Flow.Publisher{}}}, or {{{}Iterable{}}}, resolving each element asynchronously|
|Yield return|{{yield return expr}}|Emits a value from an async generator method, producing an {{AsyncStream<T>}} with natural back-pressure|
|Defer|defer \{ cleanup }|Schedules a cleanup block to execute on method exit (LIFO order), inspired by Go's {{defer}}|
|@Async annotation|{{@Async}} on method declarations|Annotation equivalent of the {{async}} keyword modifier, for interoperability with annotation-processing tools|
h3. Public API ({{{}groovy.concurrent{}}} package)
||Class/Interface||Role||
|{{Awaitable<T>}}|Core promise abstraction (analogous to C#'s {{Task<T>}} / JS's {{{}Promise<T>{}}}). Provides static combinators ({{{}all{}}}, {{{}any{}}}, {{{}allSettled{}}}, {{{}delay{}}}, {{{}timeout{}}}, {{{}timeoutOr{}}}), factories ({{{}of{}}}, {{{}failed{}}}), and instance continuation methods ({{{}then{}}}, {{{}thenCompose{}}}, {{{}exceptionally{}}}, {{{}orTimeout{}}}, {{{}completeOnTimeout{}}})|
|{{AsyncStream<T>}}|Asynchronous iteration abstraction (analogous to C#'s {{{}IAsyncEnumerable<T>{}}}). Supports {{{}moveNext(){}}}/{{{}getCurrent(){}}} pull-based consumption with {{AutoCloseable}} semantics|
|{{AwaitResult<T>}}|Outcome wrapper returned by {{allSettled()}} — carries either a success value or a failure throwable|
|{{AwaitableAdapter}}|SPI interface for adapting third-party async types (RxJava, Reactor, Spring) to {{{}Awaitable{}}}/{{{}AsyncStream{}}}|
|{{AwaitableAdapterRegistry}}|Central adapter registry with {{ServiceLoader}} auto-discovery and runtime registration|
h3. Internal Runtime ({{{}org.apache.groovy.runtime.async{}}} package)
||Class||Role||
|{{AsyncSupport}}|Central runtime entry point — all {{await}} overloads, {{async}} execution, {{defer}} scope management, combinator implementation, timeout scheduling, {{yield return}} dispatch|
|{{GroovyPromise<T>}}|Default {{Awaitable}} implementation backed by {{{}CompletableFuture{}}}. Sole bridge between the public API and JDK async infrastructure|
|{{AsyncStreamGenerator<T>}}|Producer/consumer {{AsyncStream}} implementation using {{SynchronousQueue}} for zero-buffered back-pressure with cooperative cancellation via thread tracking|
h2. Design Principles
 # *Readability first.* Async code should be visually indistinguishable from synchronous code. All standard Groovy control-flow constructs ({{{}try{}}}/{{{}catch{}}}, {{{}for{}}}, {{{}if{}}}/{{{}else{}}}, closures) must work naturally inside async methods.
 # *Exception transparency.* {{await}} automatically unwraps {{{}CompletionException{}}}, {{{}ExecutionException{}}}, and other JVM wrapper layers. The original exception type, message, and stack trace are preserved — callers see the same exceptions they would in synchronous code.
 # *API decoupling.* User code depends on {{{}Awaitable{}}}, not on {{{}CompletableFuture{}}}. The public API ({{{}groovy.concurrent{}}}) is separated from the internal implementation ({{{}org.apache.groovy.runtime.async{}}}). If the JDK's async infrastructure evolves (e.g., structured concurrency), only the internal layer changes.
 # *Minimal grammar footprint.* {{{}async{}}}, {{{}await{}}}, {{{}defer{}}}, and {{yield}} are contextual keywords — they remain valid identifiers in non-async contexts, preserving backward compatibility. Grammar changes to {{GroovyLexer.g4}} and {{GroovyParser.g4}} are minimal.
 # *Thread safety is the framework's responsibility.* All concurrency control (atomics, volatile, CAS) is encapsulated in the runtime. Application code never needs explicit locks, synchronization, or volatile annotations.
 # *JVM ecosystem integration.* Built-in adapters handle {{{}CompletableFuture{}}}, {{{}CompletionStage{}}}, {{{}Future{}}}, and {{Flow.Publisher}} out of the box. Third-party frameworks integrate via the {{AwaitableAdapterRegistry}} SPI.

h2. Execution Model

On {*}JDK 21+{*}, each {{async}} method runs on a virtual thread. When the thread blocks on {{{}await{}}}, the JVM parks the virtual thread and releases the carrier (OS) thread. This achieves the practical scalability of compiler-generated state machines (as in C# and Kotlin) without requiring control-flow rewriting — stack traces remain complete, and standard debuggers work unmodified.

On {*}JDK 17–20{*}, a bounded cached thread pool (default 256, configurable via {{{}groovy.async.parallelism{}}}) with a caller-runs back-pressure policy provides stable performance.

The executor is fully configurable at runtime via {{{}Awaitable.setExecutor(executor){}}}.
h2. Key Features in Detail
h3. Combinators
||Method||Analogy||Behavior||
|{{Awaitable.all(a, b, c)}}|{{Promise.all()}} / {{Task.WhenAll()}}|Waits for all; fails fast on first error|
|{{Awaitable.any(a, b)}}|{{Promise.race()}} / {{Task.WhenAny()}}|Returns the first to complete|
|{{Awaitable.allSettled(a, b)}}|{{Promise.allSettled()}}|Waits for all; captures both successes and failures in {{AwaitResult}} list|
|{{Awaitable.delay(ms)}}|{{Task.Delay()}} / {{setTimeout}}|Non-blocking timer|
|{{Awaitable.timeout(source, ms)}}|{{withTimeout}}|Fails with {{TimeoutException}} on expiry|
|{{Awaitable.timeoutOr(source, fallback, ms)}}|—|Completes with fallback value on expiry|
h3. Async Generators and Back-Pressure

Methods containing {{yield return}} produce an {{{}AsyncStream<T>{}}}, consumable via {{{}for await{}}}. The producer and consumer coordinate through a {{SynchronousQueue}} — the producer blocks on each {{yield return}} until the consumer pulls the next element, providing natural back-pressure without unbounded buffering. If the consumer exits early ({{{}break{}}}, {{{}return{}}}, exception), the producer thread is interrupted via cooperative cancellation, preventing resource leaks.
h3. Defer (Go-Style Cleanup)

The {{defer}} keyword schedules cleanup actions that execute in LIFO order when the enclosing method returns, regardless of success or failure. If multiple deferred blocks throw, the first exception is primary and subsequent ones are attached via {{{}addSuppressed(){}}}. This provides deterministic resource cleanup without deeply nested {{{}try{}}}/{{{}finally{}}} blocks.
h3. Adapter Registry (Third-Party Integration)

The {{AwaitableAdapterRegistry}} is an SPI-based extension point. Adapters can be registered:
 * At class-load time via {{ServiceLoader}} ({{{}META-INF/services/groovy.concurrent.AwaitableAdapter{}}})
 * At runtime via {{AwaitableAdapterRegistry.register(adapter)}}

This enables {{await}} to work transparently with RxJava {{{}Single{}}}/{{{}Observable{}}}, Reactor {{{}Mono{}}}/{{{}Flux{}}}, Spring {{{}ListenableFuture{}}}, or any custom async type — a single {{await}} keyword, regardless of the underlying library.
h2. Thread Safety Mechanisms

All concurrency control is internal and transparent to users:
 * *Lock-free synchronization* — {{volatile}} fields, {{{}AtomicBoolean{}}}, {{{}AtomicReference{}}}, {{CopyOnWriteArrayList}} used throughout; no {{synchronized}} blocks
 * *TOCTOU prevention* — {{AsyncStreamGenerator.moveNext()}} employs a double-check pattern after registering the consumer thread, closing a race window with concurrent {{close()}}
 * *Cooperative cancellation* — {{close()}} atomically sets a closed flag and interrupts both producer and consumer threads via tracked {{AtomicReference<Thread>}}
 * *Signal delivery under interrupt* — If {{{}complete(){}}}/{{{}error(){}}} is interrupted and the non-blocking fallback fails, the generator force-closes to prevent consumer thread leak
 * *Idempotent close* — All close operations use {{compareAndSet}} and are safe to call multiple times from any thread

h2. Cross-Language Comparison
||Aspect||Groovy||JavaScript||C#||Kotlin||Swift||
|Async declaration|async foo() \{ }|{{async function foo()}}|{{async Task<T> Foo()}}|{{suspend fun foo(): T}}|{{func foo() async throws -> T}}|
|Await|{{await expr}}|{{await expr}}|{{await expr}}|{{deferred.await()}}|{{try await expr}}|
|Async iteration|{{for await (x in src)}}|{{for await (x of src)}}|{{await foreach (x in src)}}|manual ({{{}Flow.collect{}}})|{{for try await x in seq}}|
|Async generator|{{yield return expr}}|{{yield}} in {{async function*}}|{{yield return}} in {{IAsyncEnumerable}}|flow { emit( x ) }|AsyncStream { yield( x ) }|
|Defer|defer \{ ... }|_(none)_|{{await using}}|{{use}}|{{defer}}|
|Implementation|Thread-per-task (VT on 21+)|Event loop|State machine|Coroutine SM|Async SM|
h2. Backward Compatibility
 * {{{}async{}}}, {{{}await{}}}, {{{}defer{}}}, and {{yield}} are *contextual keywords* — they act as keywords only in specific syntactic positions and remain valid identifiers elsewhere. Existing code using these as variable names, method names, or field names continues to compile and run without modification.
 * No existing public APIs are modified or removed.
 * The feature is purely additive: code that does not use {{{}async{}}}/{{{}await{}}} is entirely unaffected.

  was:
Here is an example to show proposed syntax and backend API(Java's {{CompletableFuture}} or GPars's {{{}Promise{}}}), but I think it's better for Groovy to have its own {{Promise}} to decouple with Java API because async/await as a language feature should be as stable as possible.

{{async}} will generate the {{Awaitable}} instance such as Groovy {{Promise}} implementing the {{Awaitable}} interface, and {{await}} can wait for any {{Awaitable}} instance to complete and unwrap it for the result. 
{code:java}
/**
 * 1. An async function that simulates a network API call.
 * The 'async' keyword implies it runs asynchronously without blocking.
 */
async fetchUserData(userId) {
    println "Starting to fetch data for user ${userId}..."
    
    // Simulate a 1-second network delay.
    Thread.sleep(1000) 
    
    println "Fetch successful!"
    // The 'async' function implicitly returns a "CompletableFuture" or "Promise" containing this value.
    return [userId: userId, name: 'Daniel']
}

/**
 * 2. An async function that uses 'await' to consume the result.
 */
async processUserData() {
    println "Process started, preparing to fetch user data..."
    
    try {
        // 'await' pauses this function until fetchUserData completes
        // and returns the final result directly.
        def user = await fetchUserData(1)
        
        println "Data received: ${user}"
        return "Processing complete for ${user.name}."
        
    } catch (Exception e) {
        return "An error occurred: ${e.message}"
    }
}


// --- Execution ---
println "Script starting..."

// Kick off the entire asynchronous process.
def future = processUserData()

// This line executes immediately, proving the process is non-blocking.
println "Script continues to run while user data is being fetched in the background..."

def result = future.get()

println "Script finished: ${result}"
{code}
Use async/await with closure or lambda expression:
{code}
// use closure
def c = async {
    println "Process started, preparing to fetch user data..."
    
    try {
        // 'await' pauses this function until fetchUserData completes
        // and returns the final result directly.
        def user = await fetchUserData(1)
        
        println "Data received: ${user}"
        return "Processing complete for ${user.name}."
        
    } catch (Exception e) {
        return "An error occurred: ${e.message}"
    }
}

def future = c()
{code}

{code}
// use lambda expression
def c = async () -> {
    println "Process started, preparing to fetch user data..."
    
    try {
        // 'await' pauses this function until fetchUserData completes
        // and returns the final result directly.
        def user = await fetchUserData(1)
        
        println "Data received: ${user}"
        return "Processing complete for ${user.name}."
        
    } catch (Exception e) {
        return "An error occurred: ${e.message}"
    }
}

def future = c()
 {code}


> Support async/await like ES7
> ----------------------------
>
>                 Key: GROOVY-9381
>                 URL: https://issues.apache.org/jira/browse/GROOVY-9381
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Daniel Sun
>            Priority: Major
>
> h1. Add native async/await support
> h2. Summary
> Introduce first-class {{{}async{}}}/{{{}await{}}} language support to Groovy, enabling developers to write asynchronous code in a sequential, readable style — on par with the async/await facilities in JavaScript (ES2017), C# (5.0), Kotlin (coroutines), and Swift (5.5).
> h2. Motivation
> Modern JVM applications are overwhelmingly concurrent. Web services, data pipelines, and reactive systems spend most of their time waiting for network I/O, database queries, or downstream services. The JVM offers powerful but low-level concurrency primitives ({{{}CompletableFuture{}}}, {{{}Flow.Publisher{}}}, {{{}ExecutorService{}}}), and while libraries like RxJava and Project Reactor raise the abstraction level, they introduce their own learning curve and cannot alter the language's control-flow syntax.
> Today, a typical three-step async workflow in Groovy looks like this:
> {code:groovy}
> CompletableFuture.supplyAsync { fetchUserId() }
>     .thenCompose { id -> CompletableFuture.supplyAsync { lookupName(id) } }
>     .thenCompose { name -> CompletableFuture.supplyAsync { loadProfile(name) } }
>     .exceptionally { ex -> handleError(ex) }
> {code}
> The business logic is obscured by plumbing. Exception handling is decoupled from the code that raises exceptions, and the control flow reads inside-out.
> With the proposed {{{}async{}}}/{{{}await{}}}, the same logic becomes:
> {code:groovy}
> async fetchProfile() {
>     def id   = await fetchUserIdAsync()
>     def name = await lookupNameAsync(id)
>     return await loadProfileAsync(name)
> }
> {code}
> This reads identically to synchronous code. Standard {{{}try{}}}/{{{}catch{}}}, {{{}for{}}}, {{{}if{}}}, and variable assignment all compose naturally — no callbacks, no chained lambdas.
> h2. Scope
> This proposal introduces the following language constructs and runtime APIs:
> h3. Language Constructs
> ||Construct||Syntax||Description||
> |Async method|async ReturnType methodName(params) \{ ... }|Declares a method that executes asynchronously and returns {{Awaitable<T>}}|
> |Await expression|{{await expr}} / {{await(expr)}}|Suspends the enclosing async context until the awaited computation completes; transparently unwraps the result|
> |Async closure / lambda|async \{ params -> body } / async (params) -> \{ body }|Creates a reusable asynchronous function (must be explicitly invoked to start execution)|
> |For-await loop|for await (item in source) \{ ... }|Iterates over an {{{}AsyncStream{}}}, {{{}Flow.Publisher{}}}, or {{{}Iterable{}}}, resolving each element asynchronously|
> |Yield return|{{yield return expr}}|Emits a value from an async generator method, producing an {{AsyncStream<T>}} with natural back-pressure|
> |Defer|defer \{ cleanup }|Schedules a cleanup block to execute on method exit (LIFO order), inspired by Go's {{defer}}|
> |@Async annotation|{{@Async}} on method declarations|Annotation equivalent of the {{async}} keyword modifier, for interoperability with annotation-processing tools|
> h3. Public API ({{{}groovy.concurrent{}}} package)
> ||Class/Interface||Role||
> |{{Awaitable<T>}}|Core promise abstraction (analogous to C#'s {{Task<T>}} / JS's {{{}Promise<T>{}}}). Provides static combinators ({{{}all{}}}, {{{}any{}}}, {{{}allSettled{}}}, {{{}delay{}}}, {{{}timeout{}}}, {{{}timeoutOr{}}}), factories ({{{}of{}}}, {{{}failed{}}}), and instance continuation methods ({{{}then{}}}, {{{}thenCompose{}}}, {{{}exceptionally{}}}, {{{}orTimeout{}}}, {{{}completeOnTimeout{}}})|
> |{{AsyncStream<T>}}|Asynchronous iteration abstraction (analogous to C#'s {{{}IAsyncEnumerable<T>{}}}). Supports {{{}moveNext(){}}}/{{{}getCurrent(){}}} pull-based consumption with {{AutoCloseable}} semantics|
> |{{AwaitResult<T>}}|Outcome wrapper returned by {{allSettled()}} — carries either a success value or a failure throwable|
> |{{AwaitableAdapter}}|SPI interface for adapting third-party async types (RxJava, Reactor, Spring) to {{{}Awaitable{}}}/{{{}AsyncStream{}}}|
> |{{AwaitableAdapterRegistry}}|Central adapter registry with {{ServiceLoader}} auto-discovery and runtime registration|
> h3. Internal Runtime ({{{}org.apache.groovy.runtime.async{}}} package)
> ||Class||Role||
> |{{AsyncSupport}}|Central runtime entry point — all {{await}} overloads, {{async}} execution, {{defer}} scope management, combinator implementation, timeout scheduling, {{yield return}} dispatch|
> |{{GroovyPromise<T>}}|Default {{Awaitable}} implementation backed by {{{}CompletableFuture{}}}. Sole bridge between the public API and JDK async infrastructure|
> |{{AsyncStreamGenerator<T>}}|Producer/consumer {{AsyncStream}} implementation using {{SynchronousQueue}} for zero-buffered back-pressure with cooperative cancellation via thread tracking|
> h2. Design Principles
>  # *Readability first.* Async code should be visually indistinguishable from synchronous code. All standard Groovy control-flow constructs ({{{}try{}}}/{{{}catch{}}}, {{{}for{}}}, {{{}if{}}}/{{{}else{}}}, closures) must work naturally inside async methods.
>  # *Exception transparency.* {{await}} automatically unwraps {{{}CompletionException{}}}, {{{}ExecutionException{}}}, and other JVM wrapper layers. The original exception type, message, and stack trace are preserved — callers see the same exceptions they would in synchronous code.
>  # *API decoupling.* User code depends on {{{}Awaitable{}}}, not on {{{}CompletableFuture{}}}. The public API ({{{}groovy.concurrent{}}}) is separated from the internal implementation ({{{}org.apache.groovy.runtime.async{}}}). If the JDK's async infrastructure evolves (e.g., structured concurrency), only the internal layer changes.
>  # *Minimal grammar footprint.* {{{}async{}}}, {{{}await{}}}, {{{}defer{}}}, and {{yield}} are contextual keywords — they remain valid identifiers in non-async contexts, preserving backward compatibility. Grammar changes to {{GroovyLexer.g4}} and {{GroovyParser.g4}} are minimal.
>  # *Thread safety is the framework's responsibility.* All concurrency control (atomics, volatile, CAS) is encapsulated in the runtime. Application code never needs explicit locks, synchronization, or volatile annotations.
>  # *JVM ecosystem integration.* Built-in adapters handle {{{}CompletableFuture{}}}, {{{}CompletionStage{}}}, {{{}Future{}}}, and {{Flow.Publisher}} out of the box. Third-party frameworks integrate via the {{AwaitableAdapterRegistry}} SPI.
> h2. Execution Model
> On {*}JDK 21+{*}, each {{async}} method runs on a virtual thread. When the thread blocks on {{{}await{}}}, the JVM parks the virtual thread and releases the carrier (OS) thread. This achieves the practical scalability of compiler-generated state machines (as in C# and Kotlin) without requiring control-flow rewriting — stack traces remain complete, and standard debuggers work unmodified.
> On {*}JDK 17–20{*}, a bounded cached thread pool (default 256, configurable via {{{}groovy.async.parallelism{}}}) with a caller-runs back-pressure policy provides stable performance.
> The executor is fully configurable at runtime via {{{}Awaitable.setExecutor(executor){}}}.
> h2. Key Features in Detail
> h3. Combinators
> ||Method||Analogy||Behavior||
> |{{Awaitable.all(a, b, c)}}|{{Promise.all()}} / {{Task.WhenAll()}}|Waits for all; fails fast on first error|
> |{{Awaitable.any(a, b)}}|{{Promise.race()}} / {{Task.WhenAny()}}|Returns the first to complete|
> |{{Awaitable.allSettled(a, b)}}|{{Promise.allSettled()}}|Waits for all; captures both successes and failures in {{AwaitResult}} list|
> |{{Awaitable.delay(ms)}}|{{Task.Delay()}} / {{setTimeout}}|Non-blocking timer|
> |{{Awaitable.timeout(source, ms)}}|{{withTimeout}}|Fails with {{TimeoutException}} on expiry|
> |{{Awaitable.timeoutOr(source, fallback, ms)}}|—|Completes with fallback value on expiry|
> h3. Async Generators and Back-Pressure
> Methods containing {{yield return}} produce an {{{}AsyncStream<T>{}}}, consumable via {{{}for await{}}}. The producer and consumer coordinate through a {{SynchronousQueue}} — the producer blocks on each {{yield return}} until the consumer pulls the next element, providing natural back-pressure without unbounded buffering. If the consumer exits early ({{{}break{}}}, {{{}return{}}}, exception), the producer thread is interrupted via cooperative cancellation, preventing resource leaks.
> h3. Defer (Go-Style Cleanup)
> The {{defer}} keyword schedules cleanup actions that execute in LIFO order when the enclosing method returns, regardless of success or failure. If multiple deferred blocks throw, the first exception is primary and subsequent ones are attached via {{{}addSuppressed(){}}}. This provides deterministic resource cleanup without deeply nested {{{}try{}}}/{{{}finally{}}} blocks.
> h3. Adapter Registry (Third-Party Integration)
> The {{AwaitableAdapterRegistry}} is an SPI-based extension point. Adapters can be registered:
>  * At class-load time via {{ServiceLoader}} ({{{}META-INF/services/groovy.concurrent.AwaitableAdapter{}}})
>  * At runtime via {{AwaitableAdapterRegistry.register(adapter)}}
> This enables {{await}} to work transparently with RxJava {{{}Single{}}}/{{{}Observable{}}}, Reactor {{{}Mono{}}}/{{{}Flux{}}}, Spring {{{}ListenableFuture{}}}, or any custom async type — a single {{await}} keyword, regardless of the underlying library.
> h2. Thread Safety Mechanisms
> All concurrency control is internal and transparent to users:
>  * *Lock-free synchronization* — {{volatile}} fields, {{{}AtomicBoolean{}}}, {{{}AtomicReference{}}}, {{CopyOnWriteArrayList}} used throughout; no {{synchronized}} blocks
>  * *TOCTOU prevention* — {{AsyncStreamGenerator.moveNext()}} employs a double-check pattern after registering the consumer thread, closing a race window with concurrent {{close()}}
>  * *Cooperative cancellation* — {{close()}} atomically sets a closed flag and interrupts both producer and consumer threads via tracked {{AtomicReference<Thread>}}
>  * *Signal delivery under interrupt* — If {{{}complete(){}}}/{{{}error(){}}} is interrupted and the non-blocking fallback fails, the generator force-closes to prevent consumer thread leak
>  * *Idempotent close* — All close operations use {{compareAndSet}} and are safe to call multiple times from any thread
> h2. Cross-Language Comparison
> ||Aspect||Groovy||JavaScript||C#||Kotlin||Swift||
> |Async declaration|async foo() \{ }|{{async function foo()}}|{{async Task<T> Foo()}}|{{suspend fun foo(): T}}|{{func foo() async throws -> T}}|
> |Await|{{await expr}}|{{await expr}}|{{await expr}}|{{deferred.await()}}|{{try await expr}}|
> |Async iteration|{{for await (x in src)}}|{{for await (x of src)}}|{{await foreach (x in src)}}|manual ({{{}Flow.collect{}}})|{{for try await x in seq}}|
> |Async generator|{{yield return expr}}|{{yield}} in {{async function*}}|{{yield return}} in {{IAsyncEnumerable}}|flow { emit( x ) }|AsyncStream { yield( x ) }|
> |Defer|defer \{ ... }|_(none)_|{{await using}}|{{use}}|{{defer}}|
> |Implementation|Thread-per-task (VT on 21+)|Event loop|State machine|Coroutine SM|Async SM|
> h2. Backward Compatibility
>  * {{{}async{}}}, {{{}await{}}}, {{{}defer{}}}, and {{yield}} are *contextual keywords* — they act as keywords only in specific syntactic positions and remain valid identifiers elsewhere. Existing code using these as variable names, method names, or field names continues to compile and run without modification.
>  * No existing public APIs are modified or removed.
>  * The feature is purely additive: code that does not use {{{}async{}}}/{{{}await{}}} is entirely unaffected.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)