You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/04/29 10:13:50 UTC

(groovy-website) branch asf-site updated: update release notes

This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new f0047f7  update release notes
f0047f7 is described below

commit f0047f7016a84944e5039cf397caae1468e65a5b
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Apr 29 20:13:40 2026 +1000

    update release notes
---
 site/src/site/releasenotes/groovy-6.0.adoc | 313 ++++++++++++++++++++++++++++-
 site/src/site/sitemap-user.groovy          |  16 +-
 2 files changed, 321 insertions(+), 8 deletions(-)

diff --git a/site/src/site/releasenotes/groovy-6.0.adoc b/site/src/site/releasenotes/groovy-6.0.adoc
index 0ca8ec5..a028df2 100644
--- a/site/src/site/releasenotes/groovy-6.0.adoc
+++ b/site/src/site/releasenotes/groovy-6.0.adoc
@@ -23,13 +23,18 @@ TBD
 
 == New Modules
 
-Groovy 6 ships seven new optional modules, plus a long-standing piece
+Groovy 6 ships eight new optional modules, plus a long-standing piece
 of core (Grape's Ivy backend) is split out into its own module:
 
 [cols="1,3", options="header"]
 |===
 | Module | Purpose
 
+| `groovy-concurrent-java`
+| Standalone Java library exposing the `groovy.concurrent` toolkit
+(structured concurrency, actors, agents, dataflow, pools) without
+the Groovy runtime -- see <<concurrent-java>>
+
 | `groovy-csv`
 | CSV parsing and writing via Jackson CSV
 
@@ -223,6 +228,311 @@ AsyncScope.run {
 | Pluggable; default auto-selects virtual threads or cached pool
 |===
 
+== Integrated Concurrency and Parallel Processing (incubating)
+
+Groovy 6 brings a unified concurrency and parallel-processing toolkit
+into core under
+link:../wiki/GEP-18.html[GEP-18: Integrated Concurrency and Parallel Processing]
+(https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952],
+https://issues.apache.org/jira/browse/GROOVY-11953[GROOVY-11953]).
+The new abstractions live in the `groovy.concurrent` package and
+modernise patterns from
+http://gpars.org/[GPars] around virtual threads, structured concurrency,
+and Groovy 6's `async`/`await`. The same combinators
+(`await`, `Awaitable.all`, `for await`) work uniformly across all of
+them — agents, actors, dataflow variables, channels, and parallel
+collections — so you compose features rather than learning separate
+APIs.
+
+For Java-only consumers, the same APIs are also published as the
+new `groovy-concurrent-java` module — see <<concurrent-java>>.
+
+=== Agents — thread-safe mutable state
+
+An gapi:groovy.concurrent.Agent[Agent] wraps a value and serialises
+updates via functions, eliminating data races by design. Reads compose
+with `await`:
+
+[source,groovy]
+----
+import groovy.concurrent.Agent
+
+def counter = Agent.create(0)
+counter.send { it + 1 }
+counter.send { it + 1 }
+counter.send { it + 1 }
+
+assert await(counter.getAsync()) == 3
+----
+
+An agent also exposes its update stream as a
+`java.util.concurrent.Flow.Publisher` via `changes()`, so `for await`
+consumes state transitions directly:
+
+[source,groovy]
+----
+def agent = Agent.create(0)
+async {
+    3.times { agent.send { it + 1 } }
+    agent.shutdown()
+}
+def seen = []
+for await (v in agent.changes()) { seen << v }
+assert seen == [1, 2, 3]
+----
+
+=== `@ActiveObject` — actor semantics with class syntax
+
+A hand-written actor expresses concurrency by encoding a message
+protocol — typed messages, a `switch` over message kinds, and explicit
+`send`/`sendAndGet` plumbing at every call site:
+
+[source,groovy]
+----
+import groovy.concurrent.Actor
+
+// Hand-written actor — message protocol explicit
+def account = Actor.stateful(0.0) { balance, msg ->
+    switch (msg) {
+        case { it instanceof Map && it.deposit }:
+            return balance + msg.deposit
+        case { it instanceof Map && it.withdraw }:
+            if (msg.withdraw > balance) throw new RuntimeException('Insufficient funds')
+            return balance - msg.withdraw
+        default: return balance
+    }
+}
+
+account.send([deposit: 100])
+account.send([withdraw: 30])
+def balance = await(account.sendAndGet([deposit: 0]))
+assert balance == 70.0
+----
+
+To know whether the actor is being used safely, a reader (human or AI)
+has to follow each message kind through the dispatch loop and match it
+against every call site. gapi:groovy.transform.ActiveObject[@ActiveObject]
+inverts this: write a normal class, mark methods that participate in
+the actor's serialised mailbox with
+gapi:groovy.transform.ActiveMethod[@ActiveMethod], and the AST
+transform routes those calls through an internal actor:
+
+[source,groovy]
+----
+import groovy.transform.ActiveObject
+import groovy.transform.ActiveMethod
+
+@ActiveObject
+class Account {
+    private double balance = 0
+
+    @ActiveMethod
+    void deposit(double amount) { balance += amount }
+
+    @ActiveMethod
+    void withdraw(double amount) {
+        if (amount > balance) throw new RuntimeException('Insufficient funds')
+        balance -= amount
+    }
+
+    @ActiveMethod
+    double getBalance() { balance }
+}
+
+def account = new Account()
+account.deposit(100)
+account.deposit(50)
+account.withdraw(30)
+assert account.getBalance() == 120.0
+----
+
+The thread-safety contract is now explicit and local. The
+`@ActiveObject` annotation declares the concurrency model;
+`@ActiveMethod` bodies contain plain business logic; callers see
+ordinary method calls — no message types to invent, no `switch` to
+parse, no manual reply plumbing. For non-blocking use,
+`@ActiveMethod(blocking = false)` returns an `Awaitable` that plugs
+straight into `await` and `Awaitable.all`.
+
+=== Dataflow variables
+
+A gapi:groovy.concurrent.DataflowVariable[DataflowVariable] is a
+single-assignment variable: any thread that reads before it is bound
+blocks until a value is available. `DataflowVariable` implements
+`Awaitable`, so it composes directly with `await` and `async {}`
+regardless of which task binds first:
+
+[source,groovy]
+----
+import groovy.concurrent.DataflowVariable
+
+def x = new DataflowVariable()
+def y = new DataflowVariable()
+def z = new DataflowVariable()
+
+async { z << await(x) + await(y) }   // blocks until x and y bind
+async { x << 10 }                     // bind in any order
+async { y << 5 }
+
+assert await(z) == 15
+----
+
+The companion gapi:groovy.concurrent.Dataflows[Dataflows] class
+auto-creates variables on property access for an even more concise
+form (`async { df.fullName = "${df.first} ${df.last}" }`).
+
+=== Parallel collections
+
+For CPU-bound data parallelism, `Collection` gains a family of
+parallel methods backed by a `ForkJoinPool`:
+
+[source,groovy]
+----
+def squares = (1..1_000).toList().collectParallel { it * it }
+def adults  = people.findAllParallel { it.age >= 18 }
+def total   = amounts.sumParallel { a, b -> a + b }
+----
+
+gapi:groovy.concurrent.ParallelScope[ParallelScope]`.withPool`
+binds a pool for a block, and gapi:groovy.concurrent.Pool[Pool]
+offers `Pool.cpu()`, `Pool.fixed(n)`, `Pool.io()`, and
+`Pool.virtual()` factories so CPU-bound and I/O-bound workloads can
+use distinct pools without leaking into the common pool. The
+previously introduced gapi:groovy.transform.Parallel[@Parallel] loop
+annotation shares this infrastructure:
+
+[source,groovy]
+----
+import groovy.concurrent.ParallelScope
+import groovy.concurrent.Pool
+
+ParallelScope.withPool(Pool.cpu()) { scope ->
+    def hot = bigList.findAllParallel { it.score > threshold }
+    hot.eachParallel { archive(it) }
+}
+----
+
+As a rule of thumb, prefer parallel collections (or `@Parallel`) for
+CPU-bound work and `async`/`await` with virtual threads for
+I/O-bound work — `ForkJoinPool` workers are precious and should not
+be tied up in `Thread.sleep`, network calls, or blocking I/O.
+
+=== Channel composition and broadcast
+
+`AsyncChannel` (introduced with async/await) gains composable
+pipeline operations — `filter`, `map`, `merge`, `split`, `tap` — and
+gapi:groovy.concurrent.ChannelSelect[ChannelSelect] for Go-style
+multi-channel selection.
+gapi:groovy.concurrent.BroadcastChannel[BroadcastChannel] adds
+one-to-many delivery and exposes `asPublisher()` for direct
+`Flow.Publisher` interop, so any subscriber — including a `for await`
+loop — receives every message:
+
+[source,groovy]
+----
+import groovy.concurrent.BroadcastChannel
+
+def broadcast = BroadcastChannel.create()
+def publisher = broadcast.asPublisher()
+
+async {
+    ['hello', 'world'].each { broadcast.send(it) }
+    broadcast.close()
+}
+
+for await (msg in publisher) { println msg }   // hello, world
+----
+
+The same `for await` works against any JDK `Flow.Publisher`, Reactor
+`Flux` (via `groovy-reactor`), or RxJava `Observable` (via
+`groovy-rxjava`).
+
+[[concurrent-java]]
+=== Java-only module: `groovy-concurrent-java`
+
+For Java, Kotlin, and other JVM languages that want the same
+toolkit without the full Groovy runtime, the standalone
+`groovy-concurrent-java` module exposes `AsyncScope`, `Pool`,
+`ParallelScope`, `Actor`, `Agent`, `DataflowVariable`,
+`AsyncChannel`, `BroadcastChannel`, and `ChannelSelect` against
+plain `java.util.function` types. The Groovy-only sugar
+(`async`/`await` keywords, `for await`, `defer`, `@ActiveObject`,
+`Dataflows`, parallel extension methods) requires the full Groovy
+dependency.
+
+[source,java]
+----
+import groovy.concurrent.AsyncScope;
+import org.apache.groovy.runtime.async.AsyncSupport;
+
+var result = AsyncScope.withScope(scope -> {
+    var a = scope.async(() -> fetchUser(id));
+    var b = scope.async(() -> fetchOrders(id));
+    return Map.of(
+        "user",   AsyncSupport.await(a),
+        "orders", AsyncSupport.await(b)
+    );
+});
+----
+
+The module ships under the `org.apache.groovy:groovy-concurrent-java`
+coordinate and is mutually exclusive with the full `groovy` runtime
+(Gradle enforces this via a shared capability; a runtime warning
+fires if both jars are detected on the classpath).
+
+=== Feature summary
+
+[cols="2,3,1", options="header"]
+|===
+| Feature | Description | Ticket
+
+| `Agent`
+| Thread-safe mutable value updated via serialised functions; exposes
+a `Flow.Publisher` of state changes via `changes()`.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952],
+https://issues.apache.org/jira/browse/GROOVY-11953[GROOVY-11953]
+
+| `Actor.reactor` / `Actor.stateful`
+| Stateless and stateful actors with `Awaitable`-based reply via
+`sendAndGet`.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `@ActiveObject` / `@ActiveMethod`
+| AST-driven actor semantics with class syntax. `blocking = false`
+returns an `Awaitable`.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `DataflowVariable`, `Dataflows`
+| Single-assignment variables; `Dataflows` auto-creates on property
+access. Implements `Awaitable` and supports `then` chaining.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `AsyncChannel.filter` / `map` / `merge` / `split` / `tap`
+| Composable channel pipelines.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `BroadcastChannel`, `ChannelSelect`
+| One-to-many broadcast (with `asPublisher()` for Reactive Streams);
+Go-style multi-channel select.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952],
+https://issues.apache.org/jira/browse/GROOVY-11953[GROOVY-11953]
+
+| Parallel collection methods
+| `collectParallel`, `findAllParallel`, `eachParallel`, `sumParallel`,
+`injectParallel`, `groupByParallel`, etc., on `Collection`.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `Pool` and `ParallelScope`
+| `Pool.cpu()`, `Pool.fixed(n)`, `Pool.io()`, `Pool.virtual()` pool
+factories; `ParallelScope.withPool { }` binds a pool for a block.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+
+| `groovy-concurrent-java`
+| Standalone Java module exposing the same concurrent APIs without
+the Groovy runtime.
+| https://issues.apache.org/jira/browse/GROOVY-11952[GROOVY-11952]
+|===
+
 == HttpBuilder: HTTP Client Module (incubating)
 
 Groovy 6 introduces a new `groovy-http-builder` module
@@ -1762,7 +2072,6 @@ to standard `JFrame`/`JWindow` alternatives.
 
 * Performance
 * Spec improvements
-* Further subprojects, e.g. maybe GPars
 
 [[Groovy6.0-requirements]]
 == JDK requirements
diff --git a/site/src/site/sitemap-user.groovy b/site/src/site/sitemap-user.groovy
index 9f4ed32..7eb5558 100644
--- a/site/src/site/sitemap-user.groovy
+++ b/site/src/site/sitemap-user.groovy
@@ -119,12 +119,16 @@ documentation {
     }
 
     section('Diving Deeper', 'fa-binoculars') {
-        item 'Runtime and compile-time metaprogramming',   'metaprogramming',  'core-metaprogramming'
-        item 'Domain-Specific Languages',                  'dsls',             'core-domain-specific-languages'
-        item 'Design patterns in Groovy',                  'design-patterns',  'design-patterns-in-groovy'
-        item 'Concurrency in Groovy',                      'concurrency',      'core-async-await'
-        item 'Integrating Groovy into applications',       'integrating',      'guide-integrating'
-        item 'Security',                                   'security',         'security'
+        item 'Runtime and compile-time metaprogramming',   'metaprogramming',      'core-metaprogramming'
+        item 'Domain-Specific Languages',                  'dsls',                 'core-domain-specific-languages'
+        item 'Design patterns in Groovy',                  'design-patterns',      'design-patterns-in-groovy'
+        item 'Async/await in Groovy',                      'async',                'core-async-await'
+        item 'Parallel collections',                       'parallel-collections', 'core-parallel-collections'
+        item 'Concurrent dataflow',                        'concurrent-dataflow',  'core-concurrent-dataflow'
+        item 'Concurrent actors and agents',               'concurrent-actors',    'core-concurrent-actors'
+        item 'Concurrent API for Java',                    'concurrent-java',      'core-concurrent-java'
+        item 'Integrating Groovy into applications',       'integrating',          'guide-integrating'
+        item 'Security',                                   'security',             'security'
     }
 
     section('Groovy module guides', 'fa-cubes') {