Skip to content

Improve RequiredPluginsInitializer.initialize#1711

Merged
merks merged 1 commit intoeclipse-pde:masterfrom
merks:issue-1667
Apr 3, 2025
Merged

Improve RequiredPluginsInitializer.initialize#1711
merks merged 1 commit intoeclipse-pde:masterfrom
merks:issue-1667

Conversation

@merks
Copy link
Copy Markdown
Contributor

@merks merks commented Apr 1, 2025

  • Collect deferred projects to be processed by a single job.

#1667

setSystem(true);
}

public synchronized void initialize(IJavaProject project) {
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.

does it needs to be public?

Copy link
Copy Markdown
Contributor Author

@merks merks Apr 1, 2025

Choose a reason for hiding this comment

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

Its more performant for what that matters; i.e., no bridge method to make it visible... It's in a private class so it doesn't matter either way...

@merks
Copy link
Copy Markdown
Contributor Author

merks commented Apr 1, 2025

@laeubi

I'd actually like to further improve this so that org.eclipse.pde.internal.core.RequiredPluginsInitializer.setupClasspath(IJavaProject) can operate on multiple projects and would make a single call to JavaCore.setClasspathContainer(IPath, IJavaProject[], IClasspathContainer[], IProgressMonitor).

For reasons that I cannot explain, I cannot reproduce the problem with this implementation but can easily and consistently reproduce it with the existing implementation. Maybe the timing of multiple concurrent jobs setting the classpath in parallel versus this implementation that batches them and does the sequentially? I don't know.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2025

Test Results

   285 files  ±0     285 suites  ±0   48m 43s ⏱️ + 1m 40s
 3 608 tests ±0   3 532 ✅ ±0   76 💤 ±0  0 ❌ ±0 
11 016 runs  ±0  10 785 ✅ ±0  231 💤 ±0  0 ❌ ±0 

Results for commit 21fc8c2. ± Comparison against base commit 2decd24.

♻️ This comment has been updated with latest results.

@merks
Copy link
Copy Markdown
Contributor Author

merks commented Apr 1, 2025

FYI, I'm continue to investigate all the strange behaviors that I see in JDT's handling of various resource deltas.

@merks
Copy link
Copy Markdown
Contributor Author

merks commented Apr 2, 2025

With my further optimized implementation (not yet pushed this PR) here is what I observe...

The method JavaReconciler.forceReconciling() appears to key updating the editor to remove the error markers. I have instrumented it like this:

image

That method is guarded by fIninitalProcessDone being true. That happens here in the same class which I've also instrumented:

image

Here is the output:

initialProcess start
ui job 28:19:282260600
real job 28:19:288469600
forceReconciling  fIninitalProcessDone=false
java.lang.Exception: Stack trace
	at java.base/java.lang.Thread.dumpStack(Thread.java:2210)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler.forceReconciling(JavaReconciler.java:389)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler$ElementChangedListener.elementChanged(JavaReconciler.java:172)
	at org.eclipse.jdt.internal.core.DeltaProcessor$3.run(DeltaProcessor.java:1751)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.jdt.internal.core.DeltaProcessor.notifyListeners(DeltaProcessor.java:1739)
	at org.eclipse.jdt.internal.core.DeltaProcessor.firePostChangeDelta(DeltaProcessor.java:1572)
	at org.eclipse.jdt.internal.core.DeltaProcessor.fire(DeltaProcessor.java:1548)
	at org.eclipse.jdt.internal.core.DeltaProcessor.notifyAndFire(DeltaProcessor.java:2261)
	at org.eclipse.jdt.internal.core.DeltaProcessor.resourceChanged(DeltaProcessor.java:2153)
	at org.eclipse.jdt.internal.core.DeltaProcessingState.resourceChanged(DeltaProcessingState.java:490)
	at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:321)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.java:311)
	at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(NotificationManager.java:174)
	at org.eclipse.core.internal.resources.Workspace.broadcastPostChange(Workspace.java:465)
	at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:1593)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2471)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2482)
	at org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:821)
	at org.eclipse.jdt.core.JavaCore.setClasspathContainer(JavaCore.java:6243)
	at org.eclipse.pde.internal.core.RequiredPluginsInitializer.setupClasspath(RequiredPluginsInitializer.java:122)
	at org.eclipse.pde.internal.core.RequiredPluginsInitializer$DeferredClasspathContainerInitializerJob.run(RequiredPluginsInitializer.java:73)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
initialProcess elapsedTime=3729
java.lang.Exception: Stack trace
	at java.base/java.lang.Thread.dumpStack(Thread.java:2210)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler.initialProcess(JavaReconciler.java:433)
	at org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:177)

So setting the classpath does result in the method being called, but it's happen before fIninitalProcessDone is set to true and hence is ignored. The initialization apparently takes elapsedTime=3729.

If I add a 5000ms scheduling delay, the order of events is reversed and the editor is updated properly:

initialProcess start
ui job 30:40:459639300
real job 30:40:466639900
initialProcess elapsedTime=3469
java.lang.Exception: Stack trace
	at java.base/java.lang.Thread.dumpStack(Thread.java:2210)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler.initialProcess(JavaReconciler.java:433)
	at org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:177)
forceReconciling  fIninitalProcessDone=true
java.lang.Exception: Stack trace
	at java.base/java.lang.Thread.dumpStack(Thread.java:2210)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler.forceReconciling(JavaReconciler.java:389)
	at org.eclipse.jdt.internal.ui.text.JavaReconciler$ElementChangedListener.elementChanged(JavaReconciler.java:172)
	at org.eclipse.jdt.internal.core.DeltaProcessor$3.run(DeltaProcessor.java:1751)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.jdt.internal.core.DeltaProcessor.notifyListeners(DeltaProcessor.java:1739)
	at org.eclipse.jdt.internal.core.DeltaProcessor.firePostChangeDelta(DeltaProcessor.java:1572)
	at org.eclipse.jdt.internal.core.DeltaProcessor.fire(DeltaProcessor.java:1548)
	at org.eclipse.jdt.internal.core.DeltaProcessor.notifyAndFire(DeltaProcessor.java:2261)
	at org.eclipse.jdt.internal.core.DeltaProcessor.resourceChanged(DeltaProcessor.java:2153)
	at org.eclipse.jdt.internal.core.DeltaProcessingState.resourceChanged(DeltaProcessingState.java:490)
	at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:321)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.java:311)
	at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(NotificationManager.java:174)
	at org.eclipse.core.internal.resources.Workspace.broadcastPostChange(Workspace.java:465)
	at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:1593)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2471)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2482)
	at org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:821)
	at org.eclipse.jdt.core.JavaCore.setClasspathContainer(JavaCore.java:6243)
	at org.eclipse.pde.internal.core.RequiredPluginsInitializer.setupClasspath(RequiredPluginsInitializer.java:122)
	at org.eclipse.pde.internal.core.RequiredPluginsInitializer$DeferredClasspathContainerInitializerJob.run(RequiredPluginsInitializer.java:73)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)

So the problem remains that JDT is doing expensive (block IO) things on the UI thread and is doing it too early (no jobs enabled). In addition JDT is also doing editor initialization on a background thread, not on a job that we could join. As a result, the editor is not in a state to respond properly to incoming events, ignoring them as if they never happened.

On the PDE side of things it seems we can only provide hacks.


public synchronized void initialize(IJavaProject project) {
if (projects.add(project)) {
schedule(5000);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@laeubi @iloveeclipse

Maybe this should use a system property with a default of 5000 so that someone one a slow machine could maybe configure this differently?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FYI, with the changes from eclipse-jdt/eclipse.jdt.ui#2127 the delay is not needed...

- Collect deferred projects to be processed by a single job.

eclipse-pde#1667
protected void setupClasspath(IJavaProject javaProject) throws JavaModelException {
IProject project = javaProject.getProject();
// The first project to be built may initialize the PDE models, potentially long running, so allow cancellation
protected static void setupClasspath(IJavaProject... javaProjects) throws JavaModelException {
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.

Is it really useful to change this method for the case we actually like to avoid? How many projects are there to be collected that its worth to optimize here?

Copy link
Copy Markdown
Contributor Author

@merks merks Apr 3, 2025

Choose a reason for hiding this comment

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

Yes, it can be quite a large number, 18 and 35 I've seen; all timing related of course. The overhead downstream is massive, spawning huge chains of events that if you step over it steps for a very long time. Given we are batching the projects it makes sense to me to batch the whole deal so that all the classpath updates happen as a single workspace operation that could kick off a single build. In fact there is so much overhead that when I added this optimization the problem at the JDT side of things came back because of the fewer events bubbling through the system.

@merks merks merged commit 3595d74 into eclipse-pde:master Apr 3, 2025
19 checks passed
@merks merks deleted the issue-1667 branch April 3, 2025 08:15
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.

3 participants