Skip to content

fix: prevent ConcurrentModificationException in parallel configuration cache with lazily registered reports#1656

Merged
hazendaz merged 4 commits into
masterfrom
copilot/review-issue-1654
May 2, 2026
Merged

fix: prevent ConcurrentModificationException in parallel configuration cache with lazily registered reports#1656
hazendaz merged 4 commits into
masterfrom
copilot/review-issue-1654

Conversation

Copilot AI commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

With org.gradle.configuration-cache.parallel=true, Gradle's parallel serializer iterates DefaultNamedDomainObjectCollection.UnfilteredIndex.pendingMap (a LinkedHashMap) to write pending providers. Serializing a provider value triggers its realization, which removes the entry from the map mid-iteration → ConcurrentModificationException. This manifests in multi-project builds using the configureEach + register() pattern:

tasks.withType(SpotBugsTask).configureEach {
    reports {
        register('xml') { required = true }
    }
}

Changes

  • SpotBugsTask.reports(Action) — replace reports.toList() with reports.matching { true }.toList(). The plain toList() call routes through DefaultDomainObjectCollection.iterator(), which walks only already-realized elements and never calls realizePending(), leaving registered items in pendingMap. matching { } creates a FilteredElementSource whose iterator() calls realizePending() first, fully draining pendingMap before any config-cache serializer touches the container.

  • Issue1654.groovy — regression test: two-subproject build with configureEach { reports { register('xml') ... } } and org.gradle.configuration-cache.parallel=true, asserting the cache is stored cleanly on the first run and reused on the second.

…efore config-cache serialization; add regression test Issue1654

Agent-Logs-Url: https://github.com/spotbugs/spotbugs-gradle-plugin/sessions/8f84448b-8d84-422a-b1fc-3c6bd1ba15f8

Co-authored-by: hazendaz <975267+hazendaz@users.noreply.github.com>
@hazendaz

Copy link
Copy Markdown
Member

@copilot Test failed
Issue1654 > reports.register() in configureEach does not cause ConcurrentModificationException with parallel config cache FAILED
org.gradle.testkit.runner.UnexpectedBuildFailure at Issue1654.groovy:100

Copilot AI commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Test failed
Issue1654 > reports.register() in configureEach does not cause ConcurrentModificationException with parallel config cache FAILED
org.gradle.testkit.runner.UnexpectedBuildFa...

Fixed in e2294e4. The root build.gradle used import com.github.spotbugs.snom.SpotBugsTask but the root project didn't apply the SpotBugs plugin, so the class wasn't on the root project's build script compilation classpath — causing a compile error.

Restructured the test to match the existing MultiProjectFunctionalTest pattern: each subproject (sub1, sub2) now has its own build.gradle that uses apply plugin: 'com.github.spotbugs' and configures spotbugsMain { reports { register('xml') { ... } } } directly — no import of SpotBugsTask needed.

Copilot AI requested a review from hazendaz April 16, 2026 01:54
@hazendaz

Copy link
Copy Markdown
Member

@copilot Issue1654 > reports.register() does not cause ConcurrentModificationException with parallel config cache FAILED
java.io.FileNotFoundException at Issue1654.groovy:62

Copilot AI commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Issue1654 > reports.register() does not cause ConcurrentModificationException with parallel config cache FAILED
java.io.FileNotFoundException at Issue1654.groovy:62

Fixed in 5dfb099. The subDir (sub1/sub2) didn't exist yet when build.gradle was written into it. Added subDir.mkdirs() before the file creation.

@hazendaz

hazendaz commented May 2, 2026

Copy link
Copy Markdown
Member

@copilot Current errors, see

Issue1654 > reports.register() does not cause ConcurrentModificationException with parallel config cache FAILED
    org.gradle.testkit.runner.UnexpectedBuildFailure at Issue1654.groovy:102

97 tests completed, 1 failed, 6 skipped

Please fix

Copilot AI commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Current errors, see

...

Fixed in 7d63111. The inner Gradle build was failing with Plugin with id 'com.github.spotbugs' not found. — the test had no root build.gradle, so TestKit's withPluginClasspath() injection never initialized the plugin classloader for the subprojects to use apply plugin: 'com.github.spotbugs'. Added a minimal root build.gradle with plugins { id 'com.github.spotbugs' }, matching the pattern used in all other multi-project functional tests.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx4g -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.4-bin/92wwslzcyst3phie3o264zltu/gradle-8.14.4/lib/gradle-daemon-main-8.14.4.jar (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@sonarqubecloud

sonarqubecloud Bot commented May 2, 2026

Copy link
Copy Markdown

@hazendaz hazendaz marked this pull request as ready for review May 2, 2026 19:34
@hazendaz hazendaz merged commit 3a48dc2 into master May 2, 2026
10 checks passed
@hazendaz hazendaz deleted the copilot/review-issue-1654 branch May 2, 2026 19:36
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.

2 participants