[
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)