Skip to content

Fix performance issue and infinite loop in classpath computation#2204

Open
vogella wants to merge 2 commits intoeclipse-pde:masterfrom
vogella:classpath-computation
Open

Fix performance issue and infinite loop in classpath computation#2204
vogella wants to merge 2 commits 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   36m 6s ⏱️ +42s
3 497 tests +2  3 400 ✅  - 32   54 💤 ±0  43 ❌ +34 
9 312 runs  +6  9 138 ✅  - 29  130 💤 ±0  44 ❌ +35 

For more details on these failures, see this check.

Results for commit 23f8aac. ± Comparison against base commit 0bfeb26.

♻️ 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 4 times, most recently from c4112eb to 1cac8bc Compare March 18, 2026 15:07
@vogella
Copy link
Contributor Author

vogella commented Mar 18, 2026

I do not see a new warning in https://ci.eclipse.org/pde/job/eclipse.pde/job/master/146/eclipse/

@vogella
Copy link
Contributor Author

vogella commented Mar 18, 2026

I guess is this one: the method newRequiredBundle(String, VersionRange, boolean, boolean) from the type IBundleProjectService has been deprecated since
version 3.19 (removal in 2026-09 or later) and marked for removal

Copy link
Contributor Author

@vogella vogella left a comment

Choose a reason for hiding this comment

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

The Compiler check is failing because of 1 new deprecation warning in ChainedReexportPerformanceTest.java at line 166:

The method newRequiredBundle(String, VersionRange, boolean, boolean) from the type IBundleProjectService has been deprecated since version 3.19 (removal in 2026-09 or later) and marked for removal

The issue is that passing null as the second argument makes the compiler resolve to the deprecated overload (which takes org.eclipse.osgi.service.resolver.VersionRange) instead of the non-deprecated one (which takes org.osgi.framework.VersionRange).

Fix: cast null to org.osgi.framework.VersionRange:

IRequiredBundleDescription mainReq = service.newRequiredBundle(CHAIN_PREFIX + (BUNDLE_CHAIN_DEPTH - 1), (VersionRange) null, false, true);

and add this import:

import org.osgi.framework.VersionRange;

@vogella vogella marked this pull request as ready for review March 19, 2026 08:18
@vogella
Copy link
Contributor Author

vogella commented Mar 19, 2026

Jenkins started 16 hours ago.....

image

@vogella vogella force-pushed the classpath-computation branch from bacb601 to 34acbe1 Compare March 21, 2026 10:56
vogella and others added 2 commits March 23, 2026 11:58
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>
Cast null to VersionRange (org.osgi.framework) to resolve to the
non-deprecated overload of IBundleProjectService.newRequiredBundle().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vogella vogella force-pushed the classpath-computation branch from 34acbe1 to 23f8aac Compare March 23, 2026 10:58
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