Skip to content

Add VirtualThreadPromiseFactory seed#15960

Open
jamesfredley wants to merge 1 commit into
8.0.xfrom
feat/virtual-thread-promise-factory-seed
Open

Add VirtualThreadPromiseFactory seed#15960
jamesfredley wants to merge 1 commit into
8.0.xfrom
feat/virtual-thread-promise-factory-seed

Conversation

@jamesfredley

Copy link
Copy Markdown
Contributor

Description

What was found

Problem Impact
Async still centers on older factory options including GPars path Misses Java 21 virtual threads
Codebase 3 called for VirtualThreadPromiseFactory No implementation yet
GPars should be deprecation-track for 9.0 Needs docs + modern factory first

What changed

Area Change
Factory Add VirtualThreadPromiseFactory in grails-async-core
Selection Wire through PromiseFactoryBuilder with opt-in config
Docs Async guide notes GPars deprecation direction
Tests Virtual-thread factory coverage

Out of scope / follow-up

Topic Status
Removing grails-async-gpars 9.0
Full ThreadLocal audit Separate epic (#15828 partial)
Default factory flip Decision needed

Related MD topics

Source Topic
Codebase 3 GPars deprecation / VirtualThreadPromiseFactory
Google Doc 1.3 Virtual-thread readiness

Contributor Checklist

Issue and Scope

  • Starter modern async factory.
  • Opt-in, non-breaking default path preserved.
  • Single focused seed.
  • Targets 8.0.x.

Code Quality

  • Tests added.
  • Focused async-core tests run.
  • No mass reformatting.
  • AI starting point labeled.

Licensing and Attribution

  • Apache License 2.0.
  • Contributor rights confirmed.
  • ai-generated-starting-point label applied.

Documentation

  • Async docs updated.
  • Scope explained.

Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]

Introduce virtual-thread PromiseFactory and document GPars deprecation path.

Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 seeds Java 21 virtual-thread support in grails-async by introducing a new VirtualThreadPromiseFactory, wiring it into PromiseFactoryBuilder behind an opt-in system property, and documenting the intended deprecation direction for GPars.

Changes:

  • Added VirtualThreadPromiseFactory (virtual-thread-per-task executor) to grails-async-core.
  • Updated PromiseFactoryBuilder to select the virtual-thread factory when -Dgrails.async.promiseFactory=virtual-thread is set and no ServiceLoader factory is present.
  • Updated async guide documentation and added a new Spock spec covering selection/execution.

Reviewed changes

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

File Description
grails-async/core/src/main/groovy/org/grails/async/factory/future/VirtualThreadPromiseFactory.groovy Adds the new virtual-thread-backed PromiseFactory implementation.
grails-async/core/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy Adds opt-in selection for the virtual-thread factory via system property.
grails-async/core/src/test/groovy/grails/async/VirtualThreadPromiseFactorySpec.groovy Adds tests for opt-in selection and basic execution behavior.
grails-doc/src/en/guide/async/asyncPromises.adoc Documents the opt-in virtual-thread factory and notes GPars deprecation direction.

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

Comment on lines +84 to +104
@Override
<T> Promise<List<T>> onComplete(List<Promise<T>> promises, Closure<T> callable) {
return createPromise({
List<T> values = waitAll(promises)
callable.call(values)
} as Closure<List<T>>)
}

@Override
<T> Promise<List<T>> onError(List<Promise<T>> promises, Closure<?> callable) {
return createPromise({
try {
waitAll(promises)
return null
}
catch (Throwable e) {
callable.call(e)
return e
}
} as Closure<List<T>>)
}
Comment on lines +108 to +110
void close() {
executorService.shutdown()
}

By default, the `Promises` static methods use an instance of `PromiseFactory`. This `PromiseFactory` interface has various implementations. The default implementation is link:{api}org/grails/async/factory/future/CachedThreadPoolPromiseFactory.html[CachedThreadPoolPromiseFactory] which uses a thread pool that will create threads as needed (the same as `java.util.concurrent.Executors.newCachedThreadPool()`)

Grails 8.1 also includes an opt-in Java 21 virtual-thread seed implementation, `org.grails.async.factory.future.VirtualThreadPromiseFactory`.
/**
* PromiseFactory implementation backed by Java virtual threads.
*
* @since 8.1
Comment on lines +25 to +38
class VirtualThreadPromiseFactorySpec extends Specification {

def cleanup() {
System.clearProperty('grails.async.promiseFactory')
Promises.promiseFactory = null
}

void 'builder can opt in to virtual thread promise factory'() {
given:
System.setProperty('grails.async.promiseFactory', 'virtual-thread')

expect:
PromiseFactoryBuilder.build() instanceof VirtualThreadPromiseFactory
}
@jdaugherty

Copy link
Copy Markdown
Contributor

We've previously discussed with @paulk-asert about integrating Gpars into groovy. I do not agree with it's deprecation.

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 39.28571% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.5345%. Comparing base (b00e83e) to head (5722a42).
⚠️ Report is 8 commits behind head on 8.0.x.

Files with missing lines Patch % Lines
.../factory/future/VirtualThreadPromiseFactory.groovy 26.0870% 16 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #15960        +/-   ##
==================================================
- Coverage     49.5403%   49.5345%   -0.0057%     
- Complexity      16922      16925         +3     
==================================================
  Files            1999       2000         +1     
  Lines           93754      93779        +25     
  Branches        16420      16421         +1     
==================================================
+ Hits            46446      46453         +7     
- Misses          40145      40161        +16     
- Partials         7163       7165         +2     
Files with missing lines Coverage Δ
.../grails/async/factory/PromiseFactoryBuilder.groovy 100.0000% <100.0000%> (ø)
.../factory/future/VirtualThreadPromiseFactory.groovy 26.0870% <26.0870%> (ø)

... and 2 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.

@testlens-app

testlens-app Bot commented Jul 10, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 5722a42
▶️ Tests: 7829 executed
⚪️ Checks: 60/60 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants