Skip to content

Fix performance issue and infinite loop in classpath computation#2204

Draft
vogella wants to merge 1 commit intoeclipse-pde:masterfrom
vogella:classpath-computation
Draft

Fix performance issue and infinite loop in classpath computation#2204
vogella wants to merge 1 commit intoeclipse-pde:masterfrom
vogella:classpath-computation

Conversation

@vogella
Copy link
Contributor

@vogella vogella commented Jan 22, 2026

Optimize access rule accumulation by using Set instead of List to avoid O(N) containment checks during insertion, which was causing O(N^2) complexity for plug-ins with many re-exported packages.

Additionally, add a recursion guard in findExportedPackages to prevent infinite loops when processing cyclic re-exports in implicit or secondary dependencies.

@laeubi
Copy link
Contributor

laeubi commented Jan 22, 2026

Please add meaningful title and descriptions see https://github.com/eclipse-platform/.github/blob/main/CONTRIBUTING.md#using-draft-pull-requests before opening draft PRs.

@github-actions
Copy link

github-actions bot commented Jan 22, 2026

Test Results

  147 files  ±0    147 suites  ±0   39m 1s ⏱️ + 3m 39s
3 495 tests +2  3 441 ✅ +2   54 💤 ±0  0 ❌ ±0 
9 309 runs  +6  9 179 ✅ +6  130 💤 ±0  0 ❌ ±0 

Results for commit d943a80. ± Comparison against base commit bc2c838.

♻️ This comment has been updated with latest results.

@vogella vogella changed the title Classpath computation Fix performance issue and infinite loop in classpath computation Jan 22, 2026
@vogella vogella force-pushed the classpath-computation branch 4 times, most recently from d023d5c to 6bc9ca3 Compare January 23, 2026 16:26
@vogella vogella force-pushed the classpath-computation branch from 6bc9ca3 to 6bf6825 Compare February 13, 2026 10:02
@vogella vogella force-pushed the classpath-computation branch 4 times, most recently from 6790616 to f170c65 Compare March 3, 2026 12:56
}

Map<BundleDescription, List<Rule>> map = retrieveVisiblePackagesFromState(desc);
Map<BundleDescription, Set<Rule>> map = retrieveVisiblePackagesFromState(desc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As order is important we should use a SequencedSet as generic here to make it clear also at all other places...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual classes used in the details is LinkedHashSet. Other places use Set.of() will be problematic.

Copy link
Contributor Author

@vogella vogella Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set.of() remains anywhere in the relevant context, every map value is now a new LinkedHashSet<>(). The current state is consistent and the compiler agrees with it (clean build verified earlier).

@vogella vogella force-pushed the classpath-computation branch from f170c65 to 834c24b Compare March 3, 2026 13:32
if (junitBundle.getVersion().compareTo(JUNIT_5_9) < 0) {
// JUnit 5.8 and below bundles don't have specific version requirements that we can use
junitRequirements = collectRequirements(
JUNIT5_RUNTIME_PLUGINS.stream().map(id -> PluginRegistry.findModel(id, BELOW_JUNIT_5_9)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JUNIT5_RUNTIME_PLUGINS is unstable set, stream of it is unstable too, and at line 618 we iterate over for (BundleDescription desc : junitRequirements)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bug introduced here, but should be fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@laeubi
Copy link
Contributor

laeubi commented Mar 3, 2026

By the way I'm not sure if order in general matters for these rules, and maybe JDT even do not care at all in wich case we can simply use a HashSet (and maybe sort the rules) but if a LinkedHashSet is used for a good reason... From the code even a Map<BundleDescription, Collection<Rule>> then might me more sufficient here...

@vogella vogella force-pushed the classpath-computation branch 3 times, most recently from bdbd6fa to 21a81c3 Compare March 5, 2026 09:37
@vogella vogella force-pushed the classpath-computation branch from 21a81c3 to ab78bea Compare March 15, 2026 12:31
Optimize access rule accumulation in RequiredPluginsClasspathContainer by
using Set<Rule> instead of List<Rule> to avoid O(N) containment checks
during insertion, which was causing O(N^2) complexity for plug-ins with
many re-exported packages.

Add a recursion guard in findExportedPackages to prevent infinite loops
when processing cyclic re-exports in implicit or secondary dependencies.

Add two tests:
- RequiredPluginsClasspathContainerPerformanceTest: verifies that classpath
  computation finishes quickly even when secondary dependencies form a cyclic
  re-export graph.
- ChainedReexportPerformanceTest: verifies performance in a chained re-export
  scenario to ensure no O(N^2) scaling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vogella vogella force-pushed the classpath-computation branch from ab78bea to d943a80 Compare March 16, 2026 10:21
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.

4 participants