Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer initialization of classpath to the background if called from main #1525

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

laeubi
Copy link
Contributor

@laeubi laeubi commented Dec 17, 2024

Currently there are some bad behaving UI components in the eclipse IDE that trigger resolving of the classpath containers in the UI, this leads to very bad startup performance and even deadlocks in startup.

This now detects the issue, logs a warning of the offending component and defer the initialization of classpath to a background job, this currently increase time from starting eclipse until UI is shown noticeable.

Fix #1481

@vogella
Copy link
Contributor

vogella commented Dec 17, 2024

I'm very happy to see that you working on the startup performance of Eclipse @laeubi Thank you.

Copy link

github-actions bot commented Dec 17, 2024

Test Results

  254 files   -    31    254 suites   - 31   34m 41s ⏱️ - 17m 30s
3 586 tests ±    0  3 509 ✅  -     1   77 💤 + 1  0 ❌ ±0 
7 930 runs   - 3 020  7 751 ✅  - 2 968  179 💤  - 52  0 ❌ ±0 

Results for commit 9b217a6. ± Comparison against base commit 0b123e0.

This pull request skips 1 test.
org.eclipse.pde.build.internal.tests.p2.P2Tests ‑ offBug237662

♻️ This comment has been updated with latest results.

Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

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

Thank you Christoph for working on this issue, I'm very happy about that as well.
But the presented solution does seem to be very robust and I don't think pde.core should handle issues that arise from wrong usage in the UI. Therefore I would prefer a solution in the UI classes where this is caused. E.g. the editor should defer the initialization to a background thread. I would like to have this only as a last resort solution.

Job job = JOB_MAP.get(javaProject);
if (job != null) {
try {
job.join();
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this have the effect that the method blocks again until the classpath-set returns?
How would this then improve the start-up performance? Or is it just done to resolve dead-locks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This codepath is only executed when it is called outside the UI (e.g. in a build thread) where it is fine to block, the goal is to join on a possible execution that previously was scheduled in an UI thread.

@laeubi
Copy link
Contributor Author

laeubi commented Dec 18, 2024

But the presented solution does seem to be very robust and I don't think pde.core should handle issues that arise from wrong usage in the UI.

It is not nice but reflects reality in Eclipse IDE runtime environment and we can quite easy remove it once a better solution is there. Also there are sadly numerous places (and maybe some unknown places) where this happens and at least we get a warning in the log now when new things arise.

@laeubi
Copy link
Contributor Author

laeubi commented Dec 18, 2024

Interestingly the warning even triggers in the CI build already :-D

https://ci.eclipse.org/pde/job/eclipse.pde/job/PR-1525/2/maven-warnings/new/source.b23f7a3a-eedd-4a60-b012-8767a6761747/#6521

@jukzi
Copy link
Contributor

jukzi commented Dec 18, 2024

Thanks for working on the issue. But i think one should not make decisions based on thread name. Better solve it in the caller.

@laeubi
Copy link
Contributor Author

laeubi commented Dec 18, 2024

Thanks for working on the issue. But i think one should not make decisions based on thread name. Better solve it in the caller.

Will you propose PR to do so for JDT?

And as mentioned before the worst that can happen is it will work as before, so there is no real "danger" in this decision.

@jukzi
Copy link
Contributor

jukzi commented Dec 18, 2024

Will you propose PR to do so for JDT?

no, but that is no reason to introduce dirty hacks.

@laeubi
Copy link
Contributor Author

laeubi commented Dec 18, 2024

Will you propose PR to do so for JDT?

no, but that is no reason to introduce dirty hacks.

This is not a dirty "hack" it is a workaround for the situation that is is very unlikely that changes will occur any time soon in an unrelated project (JDT) as it is "understaffed" (as every other project as well... So if we can improve the situation quite easy and make it better for all users of PDE there is no reason to wait for something that maybe never happens.

And even if it does, we can easily remove the code again without any problem.

@vogella
Copy link
Contributor

vogella commented Dec 18, 2024

I agree that we should apply this PR.

PDE should protect itself from bad callers which seems to be in this case JDT.

@iloveeclipse
Copy link
Member

I agree that we should apply this PR.
PDE should protect itself from bad callers which seems to be in this case JDT.

I'm not sure you understand possible implications of that PR. JDT will see classpath containers that are not fully initialized or use containers that can't be initialized at all. This may lead to unpredictable plugin compilation results.

Please check org.eclipse.jdt.core.ClasspathContainerInitializer.initialize(IPath, IJavaProject) javadoc and org.eclipse.jdt.internal.core.JavaModelManager.initializeContainer(IJavaProject, IPath) code.

I agree with @jukzi this is a dirty hack and dangerous one too.

@laeubi
Copy link
Contributor Author

laeubi commented Dec 18, 2024

I'm not sure you understand possible implications of that PR. JDT will see classpath containers that are not fully initialized or use containers that can't be initialized at all. This may lead to unpredictable plugin compilation results.

When the computation is done, we notify JDT that something has changed and then it will simply reevaluate. Also as compilation does not happen in the UI thread for this case nothing changes at all, this all calling from the UI is as far as I can see only to inspect the classpath entries and the init of the container is just a side-effect.

Also as you mention the javadoc it says:

Binds a classpath container to a IClasspathContainer for a given project,
or silently fails if unable to do so.

So it would be even valid to do nothing and simply return, I just wanted to be a little bit nice here ;-)

I would be happy to not require this so I suggest anyone who feels uncomfortable with this put a breakpoint in

org.eclipse.jdt.internal.core.JavaModelManager.initializeContainer(IJavaProject, IPath)

and fix all call sites that enter her in the main (== UI thread) when you start an eclipse, sadly those are very deep nested so its not trivial, and obviously those are not compile threads in any case.

Currently there are some bad behaving UI components in the eclipse IDE
that trigger resolving of the classpath containers in the UI, this leads
to very bad startup performance and even deadlocks in startup.

This now detects the issue, logs a warning of the offending component
and defer the initialization of classpath to a background job, this
currently increase time from starting eclipse until UI is shown
noticeable.

Fix eclipse-pde#1481
@laeubi
Copy link
Contributor Author

laeubi commented Dec 27, 2024

I now have made some more tests:

  1. Simply return when called from UI Thread --> The Plugin Container is not added (as expected) unless one changes something from the Manifest or something else so PDE set it again
  2. Current solution I see the container is set due to the Job I started (like if someone changes the manifest or similar)
  3. Throw an exception, then I see
eclipse.buildId=unknown
java.version=21
java.vendor=Oracle Corporation
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=de_DE
Framework arguments:  -product org.eclipse.sdk.ide

org.eclipse.jdt.core
Error
Fri Dec 27 14:15:20 CET 2024
Exception while initializing all containers

org.eclipse.core.runtime.CoreException: Called from Ui thread!
   at org.eclipse.pde.internal.core.RequiredPluginsInitializer.initialize(RequiredPluginsInitializer.java:81)
   at org.eclipse.jdt.internal.core.JavaModelManager.initializeContainer(JavaModelManager.java:3209)
   at org.eclipse.jdt.internal.core.JavaModelManager$11.run(JavaModelManager.java:3097)
   at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2457)
   at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2482)
   at org.eclipse.jdt.internal.core.JavaModelManager.initializeAllContainers(JavaModelManager.java:3155)
   at org.eclipse.jdt.internal.core.JavaModelManager.getClasspathContainer(JavaModelManager.java:2131)
   at org.eclipse.jdt.core.JavaCore.getClasspathContainer(JavaCore.java:3970)
   at org.eclipse.jdt.internal.core.JavaProject.resolveClasspath(JavaProject.java:3127)
   at org.eclipse.jdt.internal.core.JavaProject.resolveClasspath(JavaProject.java:3291)
   at org.eclipse.jdt.internal.core.JavaProject.getResolvedClasspath(JavaProject.java:2405)
   at org.eclipse.jdt.internal.core.JavaProject.buildStructure(JavaProject.java:488)
   at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:246)
   at org.eclipse.jdt.internal.core.Openable.openAncestors(Openable.java:508)
   at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:228)
   at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:569)
   at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:292)
   at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:278)
   at org.eclipse.jdt.internal.core.PackageFragmentRoot.getKind(PackageFragmentRoot.java:539)
   at org.eclipse.jdt.internal.core.PackageFragment.getKind(PackageFragment.java:367)
   at org.eclipse.jdt.internal.core.PackageFragment.validateExistence(PackageFragment.java:596)
   at org.eclipse.jdt.internal.core.Openable.exists(Openable.java:212)
   at org.eclipse.jdt.internal.core.PackageFragment.exists(PackageFragment.java:189)
   at org.eclipse.jdt.ui.StandardJavaElementContentProvider.exists(StandardJavaElementContentProvider.java:509)
   at org.eclipse.jdt.ui.StandardJavaElementContentProvider.getParent(StandardJavaElementContentProvider.java:246)
   at org.eclipse.jdt.internal.ui.navigator.JavaNavigatorContentProvider.getParent(JavaNavigatorContentProvider.java:118)
   at org.eclipse.ui.internal.navigator.extensions.SafeDelegateTreeContentProvider.getParent(SafeDelegateTreeContentProvider.java:105)
   at org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider$4.run(NavigatorContentServiceContentProvider.java:646)
   at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
   at org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider.findParents(NavigatorContentServiceContentProvider.java:623)
   at org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider.findPaths(NavigatorContentServiceContentProvider.java:594)
   at org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider.findPaths(NavigatorContentServiceContentProvider.java:603)
   at org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider.getParents(NavigatorContentServiceContentProvider.java:330)
   at org.eclipse.jface.viewers.AbstractTreeViewer.getParentElement(AbstractTreeViewer.java:1793)
   at org.eclipse.jface.viewers.TreeViewer.getParentElement(TreeViewer.java:573)
   at org.eclipse.jface.viewers.AbstractTreeViewer.internalExpand(AbstractTreeViewer.java:1733)
   at org.eclipse.jface.viewers.AbstractTreeViewer.setSelectionToWidget(AbstractTreeViewer.java:2737)
   at org.eclipse.ui.navigator.CommonViewer.setSelectionToWidget(CommonViewer.java:439)
   at org.eclipse.jface.viewers.StructuredViewer.setSelectionToWidget(StructuredViewer.java:1706)
   at org.eclipse.jface.viewers.AbstractTreeViewer.setSelectionToWidget(AbstractTreeViewer.java:3253)
   at org.eclipse.jface.viewers.StructuredViewer.setSelection(StructuredViewer.java:1662)
   at org.eclipse.jface.viewers.TreeViewer.setSelection(TreeViewer.java:1091)
   at org.eclipse.ui.navigator.CommonViewer.setSelection(CommonViewer.java:369)
   at org.eclipse.ui.navigator.CommonNavigator.selectReveal(CommonNavigator.java:385)
   at org.eclipse.ui.internal.navigator.actions.LinkEditorAction$2$1.run(LinkEditorAction.java:104)
   at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
   at org.eclipse.ui.internal.navigator.actions.LinkEditorAction$2.runInUIThread(LinkEditorAction.java:93)
   at org.eclipse.ui.progress.UIJob.lambda$0(UIJob.java:148)
   at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:40)
   at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:132)
   at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:5042)
   at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4522)
   at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
   at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
   at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
   at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
   at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:663)
   at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
   at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:570)
   at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
   at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
   at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
   at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:143)
   at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:109)
   at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:439)
   at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:271)
   at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
   at java.base/java.lang.reflect.Method.invoke(Method.java:580)
   at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:668)
   at org.eclipse.equinox.launcher.Main.basicRun(Main.java:605)
   at org.eclipse.equinox.launcher.Main.run(Main.java:1481)
   at org.eclipse.equinox.launcher.Main.main(Main.java:1454)

but the container is there because at UI startup the init of container is triggered automatically.

So an alternative could be to simply throw an exception...

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.

JDT calls PDE Classpath on startup with open Editor hindering the UI to show up
5 participants