Community
Participate
Working Groups
This problem occurs in 2.0.2 & 2.1 Firstly I import a number of plugins from wsad build. Then I delete one plugin and try to get that plugin as a source from my CVS repository. The dialog initially says "Checking out the project" the project, clearly stalls in the stage "Updating". The process takes 10 minutes. Only after that the dialog displays "Invoking Java Builder" and completes. Even after that when I delete this project from the workspace it will take the same duration of time in the stage "Updating" I printed the java core during this and this is what I get ---------------------- Exception Information --------------------------- No Exception ---------------------- System Properties ------------------------------- J2RE 1.3.1 IBM Windows 32 build cn131-20020710 .. .. ... ---------------------- XM component Dump Routine ---------------------- Full thread dump Classic VM (J2RE 1.3.1 IBM Windows 32 build cn131-20020710, native threads): "ModalContext" (TID:0x7E6EB28, sys_thread_t:0x3610B7F8, state:R, native ID:0x5D4) prio=5 at org.eclipse.jdt.internal.core.JavaProject.updateAllCycleMarkers(JavaProject.java:2258) at org.eclipse.jdt.internal.core.DeltaProcessor.updateClasspathMarkers(DeltaProcessor.java:1916) at org.eclipse.jdt.internal.core.DeltaProcessor.resourceChanged(DeltaProcessor.java:1648) at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:137) at org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java(Compiled Code)) at org.eclipse.core.runtime.Platform.run(Platform.java(Compiled Code)) at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.java(Compiled Code)) at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(NotificationManager.java:67) at org.eclipse.core.internal.resources.Workspace.broadcastChanges(Workspace.java:161) at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java(Compiled Code)) at org.eclipse.core.internal.resources.Workspace.run(Workspace.java(Compiled Code)) at org.eclipse.ui.actions.WorkspaceModifyOperation.run(WorkspaceModifyOperation.java:79) at org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager.run(RepositoryManager.java:785) at org.eclipse.team.internal.ccvs.ui.actions.CVSAction$1.run(CVSAction.java:242) at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:95) "Snapshot" (TID:0x64BC310, sys_thread_t:0x3602B360, state:CW, native ID:0x4C4) prio=5 at java.lang.Object.wait(Native Method) at org.eclipse.core.internal.resources.DelayedSnapshotRunnable.run(DelayedSnapshotRunnable.java:38) at java.lang.Thread.run(Thread.java:512) "Decoration" (TID:0x16FA908, sys_thread_t:0x35E6F608, state:CW, native ID:0x5C4) prio=1 at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:429) at org.eclipse.ui.internal.decorators.DecorationScheduler.next(DecorationScheduler.java:244) at org.eclipse.ui.internal.decorators.DecorationScheduler$3.run(DecorationScheduler.java:270) at java.lang.Thread.run(Thread.java:512) "Java indexing" (TID:0x9001A0, sys_thread_t:0x34F3CC68, state:CW, native ID:0x5A4) prio=4 at java.lang.Thread.sleep(Native Method) at org.eclipse.jdt.internal.core.search.processing.JobManager.run(JobManager.java(Compiled Code)) at java.lang.Thread.run(Thread.java:512) "Finalizer" (TID:0x901900, sys_thread_t:0x89B330, state:CW, native ID:0x5B8) prio=8 at java.lang.Object.wait(Native Method) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java(Compiled Code)) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java(Compiled Code)) "Reference Handler" (TID:0x901948, sys_thread_t:0x8993C0, state:CW, native ID:0x4E0) prio=10 at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java(Compiled Code)) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java(Compiled Code)) "Signal dispatcher" (TID:0x901990, sys_thread_t:0x892520, state:R, native ID:0x378) prio=5 "main" (TID:0x9019D8, sys_thread_t:0x7B0388, state:R, native ID:0x5DC) prio=5 at org.eclipse.swt.internal.win32.OS.WaitMessage(Native Method) at org.eclipse.swt.widgets.Display.sleep(Display.java(Compiled Code)) at org.eclipse.jface.operation.ModalContext$ModalContextThread.block(ModalContext.java(Compiled Code)) at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:255) at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:351) at org.eclipse.team.internal.ccvs.ui.actions.CVSAction.run(CVSAction.java:262) at org.eclipse.team.internal.ccvs.ui.actions.AddToWorkspaceAction.checkoutSelectionIntoWorkspaceDirectory(AddToWorkspaceAction.java:64) at org.eclipse.team.internal.ccvs.ui.actions.AddToWorkspaceAction.execute(AddToWorkspaceAction.java:60) at org.eclipse.team.internal.ccvs.ui.actions.CVSAction.run(CVSAction.java:69) at org.eclipse.ui.actions.ActionDelegate.runWithEvent(ActionDelegate.java:71) at org.eclipse.ui.internal.PluginAction.runWithEvent(PluginAction.java:240) at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:456) at org.eclipse.jface.action.ActionContributionItem.handleWidgetEvent(ActionContributionItem.java:403) at org.eclipse.jface.action.ActionContributionItem.access$0(ActionContributionItem.java:397) at org.eclipse.jface.action.ActionContributionItem$ActionListener.handleEvent(ActionContributionItem.java:72) at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java(Compiled Code)) at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java(Compiled Code)) at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java(Compiled Code)) at org.eclipse.ui.internal.Workbench.run(Workbench.java:1254) at org.eclipse.core.internal.boot.InternalBootLoader.run(InternalBootLoader.java:845) at org.eclipse.core.boot.BootLoader.run(BootLoader.java:461) at java.lang.reflect.Method.invoke(Native Method) at org.eclipse.core.launcher.Main.basicRun(Main.java:247) at org.eclipse.core.launcher.Main.run(Main.java:703) at org.eclipse.core.launcher.Main.main(Main.java:539) ---------------------- LK component Dump Routine ---------------------- Monitor pool info: Initial monitor count: 32 Minimum number of free monitors before expansion: 5 Pool will next be expanded by: 16 Current total number of monitors: 32 Current number of free monitors: 21 Monitor Pool Dump (flat & inflated object-monitors): sys_mon_t:0x0023F6B0 infl_mon_t: 0x0023F2A0: java.lang.ref.Reference$Lock@912550/912558: <unowned> Waiting to be notified: "Reference Handler" (0x8993C0) sys_mon_t:0x0023F700 infl_mon_t: 0x0023F2E0: java.lang.ref.ReferenceQueue$Lock@912160/912168: <unowned> Waiting to be notified: "Finalizer" (0x89B330) sys_mon_t:0x0023F728 infl_mon_t: 0x0023F300: org.eclipse.ui.internal.decorators.DecorationScheduler@15E8EA8/15E8EB0: <unowned> Waiting to be notified: "Decoration" (0x35E6F608) sys_mon_t:0x0023F8E0 infl_mon_t: 0x0023F460: org.eclipse.core.internal.resources.DelayedSnapshotRunnable@A2E770/A2E778: <unowned> Waiting to be notified: "Snapshot" (0x3602B360) JVM System Monitor Dump (registered monitors): Evacuation Region lock: <unowned> Heap Promotion lock: <unowned> Sleep lock: <unowned> Waiting to be notified: "Java indexing" (0x34F3CC68) Method trace lock: <unowned> Heap lock: owner "Signal dispatcher" (0x892520), entry count 1 Monitor Cache lock: owner "Signal dispatcher" (0x892520), entry count 1 JNI Pinning lock: <unowned> JNI Global Reference lock: <unowned> Classloader lock: <unowned> Binclass lock: <unowned> Monitor Registry lock: owner "Signal dispatcher" (0x892520), entry count 1 Thread queue lock: owner "Signal dispatcher" (0x892520), entry count 1 Thread identifiers (as used in flat monitors): ident 0x08 "ModalContext" (0x3610B7F8) ee 0x3610B5E8 ident 0x09 "Snapshot" (0x3602B360) ee 0x3602B150 ident 0x07 "Decoration" (0x35E6F608) ee 0x35E6F3F8 ident 0x06 "Java indexing" (0x34F3CC68) ee 0x34F3CA58 ident 0x05 "Finalizer" (0x89B330) ee 0x0089B120 ident 0x04 "Reference Handler" (0x8993C0) ee 0x008991B0 ident 0x03 "Signal dispatcher" (0x892520) ee 0x00892310 ident 0x02 "main" (0x7B0388) ee 0x007B0178 Java Object Monitor Dump (flat & inflated object-monitors): java.lang.ref.ReferenceQueue$Lock@912160/912168 locknflags 80000400 Monitor inflated infl_mon 0x0023F2E0 java.lang.ref.Reference$Lock@912550/912558 locknflags 80000200 Monitor inflated infl_mon 0x0023F2A0 org.eclipse.core.internal.resources.DelayedSnapshotRunnable@A2E770/A2E778 locknflags 80001000 Monitor inflated infl_mon 0x0023F460 org.eclipse.ui.internal.decorators.DecorationScheduler@15E8EA8/15E8EB0 locknflags 80000500 Monitor inflated infl_mon 0x0023F300 org.eclipse.team.internal.ccvs.ui.actions.AddToWorkspaceAction$1@7EA4770/7EA4778 locknflags 00080000 Flat locked by thread ident 0x08, entry count 1 ---------------------- END OF DUMP ------------------------------------- I think it is the following which takes that long. at org.eclipse.jdt.internal.core.JavaProject.updateAllCycleMarkers(JavaProject.java:2258) at org.eclipse.jdt.internal.core.DeltaProcessor.updateClasspathMarkers(DeltaProcessor.java:1916) at org.eclipse.jdt.internal.core.DeltaProcessor.resourceChanged(DeltaProcessor.java:1648) at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:137) This is a real performance bottleneck. I can reproduce this scenario as many times as possible therefore its not merely circumstantial.
Did you use 2.0.2 to produce the thread dump ? In 2.1, we think we have improved the cycle detection significantly. Also we would need exact steps to reproduce. What you are doing is close to what we do when developping Eclipse and we have not seen problems in 2.1 (in 2.0.2 it was a performance issue).
Hi Philippe, Problem: I import a lot of plugins to my Eclipse RC2 environment (problem occurs in 2.0.2 (Here it is even slower,as you have mentioned earlier) & RC1 as well) After I do that. I delete two projects, connect to my CVS repository and select the same two projects and retrieve them from the repository (where I have the source). The dialog is popped up which, after retreiving the files, tries to update classpaths. This stage of the operation takes almost 10 minutes displaying "Updating". The thread stack trace reveals Full thread dump Classic VM (J2RE 1.3.1 IBM Windows 32 build cn131-20020710, native threads): "ModalContext" (TID:0x2FF9390, sys_thread_t:0x36D9F900, state:R, native ID:0x3B8) prio=5 at org.eclipse.jdt.internal.core.JavaProject.updateAllCycleMarkers(JavaProject.java:2272) at org.eclipse.jdt.internal.core.DeltaProcessor.updateClasspathMarkers(DeltaProcessor.java:1949) at org.eclipse.jdt.internal.core.DeltaProcessor.resourceChanged(DeltaProcessor.java:1686) at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:137) at org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java(Compiled Code)) at org.eclipse.core.runtime.Platform.run(Platform.java(Compiled Code)) at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.java:152) at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(NotificationManager.java:67) at org.eclipse.core.internal.resources.Workspace.broadcastChanges(Workspace.java:161) at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java(Compiled Code)) at org.eclipse.core.internal.resources.Workspace.run(Workspace.java(Compiled Code)) at org.eclipse.ui.actions.WorkspaceModifyOperation.run(WorkspaceModifyOperation.java:79) at org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager.run(RepositoryManager.java:790) at org.eclipse.team.internal.ccvs.ui.actions.CVSAction$1.run(CVSAction.java:242) at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:95) After essentially setting up a development environment to launch Eclipse, I performed the same steps as above from my Runtime Workbench and found that the operation was taking an inordinate amount of time in executing at org.eclipse.jdt.internal.core.JavaProject.updateAllCycleMarkers(JavaProject.java:2272) This method is used to perform cycle detection when traversing classpaths of each project in the workspace. The following segment of the code for (int i = 0; i < length; i++){ JavaProject project = (JavaProject)projects[i]; if (!cycleParticipants.contains(project)){ visited.clear(); project.updateCycleParticipants(null, visited, cycleParticipants, workspaceRoot); } } Here, for each project a cycle detection algorithm is run and the cycle participants are stored in a HashSet "cycleParticipants" The method project.updateCycleParticipants performs the core operation. In the method updateCycleParticipants public void updateCycleParticipants(IClasspathEntry[] preferredClasspath, ArrayList visited, HashSet cycleParticipants, IWorkspaceRoot workspaceRoot){ visited.add(this); try { IClasspathEntry[] classpath = preferredClasspath == null ? getResolvedClasspath(true) : preferredClasspath; for (int i = 0, length = classpath.length; i < length; i++) { IClasspathEntry entry = classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){ IPath entryPath = entry.getPath(); IResource member = workspaceRoot.findMember(entryPath); if (member != null && member.getType() == IResource.PROJECT){ JavaProject project = (JavaProject)JavaCore.create((IProject)member); int index = visited.indexOf(project); if (index == -1 && cycleParticipants.contains(project)) index = visited.indexOf(this); // another loop in the cycle exists if (index >= 0) { // only consider direct participants inside the cycle for (int size = visited.size(); index < size; index++) cycleParticipants.add(visited.get(index)); } else { project.updateCycleParticipants(null, visited, cycleParticipants, workspaceRoot); } } } } } catch(JavaModelException e){ } visited.remove(this); } This method as seen maintains two objects a ArrayList named "visited" & the HashSet "cycleParticipants". It's a recursive method and what this method does is that it retrieves the classpath entries for the current project and for each entry that is of type "PROJECT" It checks to see if its on its "visited" list, If it is then it forms a cycle and the method adds the project (classpath entry) to the list of cycleParticipants As seen above these two methods since they are recursive they would encounter the same project again and again, Note that some effort at optimizing has been made by ensuring that if a project is already part of the cycleParticipant then project.updateCycleParticipants(..) is not called. But there is one problem that has been overlooked, projects whose classpath entries have been already been visited can be marked as "doneTraversing" and need not be analyzed later when another project has this project as its classpath entry. To illustrate the problem, if A --> B ---> C --> D --> E-..> | | |------------- If you are traversing the path A->B->C->D->E. Once you have finished visiting project "D" & its classpath entries (in this case "E") There is no need to visit the project "D" when traversing the path A->B->D->E..> because D has already been exhausted. Infact Since the code segment already checks, if cycleParticipants includes a project, so if it doesn't include the project then we do not need to call project.updateCyc... if the project has already been "doneTraversing". So we do not lose genuine cycle participants as well. When I did that the it went a long way to reducing the amount of time this method was taking. It took 10 seconds for the above case instead of minutes. To do that, I added another HashSet named "doneTraversing". At the end of project.updateCycleParticipants(..) when one has finished analyzing the current project and all of its classpath entries, this project can be added to "doneTraversing". Therefore if we encounter the same project later in another classpath entry one need not call the project.updateCycleParticipants(..) The following code is what I had to do public static void updateAllCycleMarkers() throws JavaModelException { ... HashSet doneTraversing = new HashSet; ArrayList visited = new ArrayList(); for (int i = 0; i < length; i++){ JavaProject project = (JavaProject)projects[i]; if (!cycleParticipants.contains(project) && !doneTraversing.contains(project)){ visited.clear(); project.updateCycleParticipants(null, visited, cycleParticipants, workspaceRoot, doneTraversing); } } doneTraversing.clear(); //flush out ... } & public void updateCycleParticipants(IClasspathEntry[] preferredClasspath, ArrayList visited, HashSet cycleParticipants, IWorkspaceRoot workspaceRoot, HashSet doneTraversing){ visited.add(this); try { IClasspathEntry[] classpath = preferredClasspath == null ? getResolvedClasspath(true) : preferredClasspath; for (int i = 0, length = classpath.length; i < length; i++) { IClasspathEntry entry = classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){ IPath entryPath = entry.getPath(); IResource member = workspaceRoot.findMember(entryPath); if (member != null && member.getType() == IResource.PROJECT){ JavaProject project = (JavaProject)JavaCore.create((IProject)member); int index = visited.indexOf(project); if (index == -1 && cycleParticipants.contains(project)) index = visited.indexOf(this); // another loop in the cycle exists if (index >= 0) { // only consider direct participants inside the cycle for (int size = visited.size(); index < size; index++) cycleParticipants.add(visited.get(index)); } else { if(!doneTraversing.contains(project)) project.updateCycleParticipants(null, visited, cycleParticipants, workspaceRoot); } } } } } catch(JavaModelException e){ } visited.remove(this); doneTraversing.add(this); } The above was happening because I had 180 projects in my workspace and almost many of them have "org.eclipse.core.boot" & "org.eclipse.core.runtime" "org..xerces" in their classpath so the method ended up trying to detect cycles and ended up with 10 levels of recursive calls for the method "updateCycleParticipants" The above is just a rudimentary fix. I don't know if there is some drawback to this approach or something Iam overlooking as I said I checked it with some scenarios and it did not eliminate any genuine cycle participants. Please let me know if the above is useful in fixing the problem.
This feels like a good performance improvement. We should not only cache cycle participants (which is fairly rare in general).
When building a WSAD workspace with *.nl1 circular plugin fragments (bug in PDE?), the fix proposal improves the speed considerably, however some of the cycle detection doesn't occur any longer. Need to investigate.
Steps: - import all plugins from WSAD (~760 binary projects) with cycle severity turned as warning - observe that lots of cycle warnings got generated (due to *.nl1 projects) - import Eclipse SDK to refresh existing projects - observe that all cycle markers are gone, though those outside Eclipse projects should have persisted - change some classpath to introduce a cycle, then all cycle markers reappear. Need to find a smaller testcase.
*** Bug 35859 has been marked as a duplicate of this bug. ***
Following cycle detection code seems to have the good performance characteristics, and not causing mentionned grief: /** * Update cycle markers for all java projects */ public static void updateAllCycleMarkers() throws JavaModelException { JavaModelManager manager = JavaModelManager.getJavaModelManager(); IJavaProject[] projects = manager.getJavaModel().getJavaProjects(); IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); HashSet cycleParticipants = new HashSet(); HashSet alreadyTraversed = new HashSet(); int length = projects.length; // compute cycle participants ArrayList prereqChain = new ArrayList(); for (int i = 0; i < length; i++){ JavaProject project = (JavaProject)projects[i]; if (!alreadyTraversed.contains(project)){ prereqChain.clear(); project.updateCycleParticipants(null, prereqChain, cycleParticipants, workspaceRoot, alreadyTraversed); } } for (int i = 0; i < length; i++){ JavaProject project = (JavaProject)projects[i]; if (cycleParticipants.contains(project)){ IMarker cycleMarker = project.getCycleMarker(); String circularCPOption = project.getOption (JavaCore.CORE_CIRCULAR_CLASSPATH, true); int circularCPSeverity = JavaCore.ERROR.equals (circularCPOption) ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING; if (cycleMarker != null) { // update existing cycle marker if needed try { int existingSeverity = ((Integer) cycleMarker.getAttribute(IMarker.SEVERITY)).intValue(); if (existingSeverity != circularCPSeverity) { cycleMarker.setAttribute (IMarker.SEVERITY, circularCPSeverity); } } catch (CoreException e) { throw new JavaModelException(e); } } else { // create new marker project.createClasspathProblemMarker( new JavaModelStatus (IJavaModelStatusConstants.CLASSPATH_CYCLE, project)); } } else { project.flushClasspathProblemMarkers(true, false); } } } /** * If a cycle is detected, then cycleParticipants contains all the project involved in this cycle (directly and indirectly), * no cycle if the set is empty (and started empty) */ public void updateCycleParticipants( IClasspathEntry[] preferredClasspath, ArrayList prereqChain, HashSet cycleParticipants, IWorkspaceRoot workspaceRoot, HashSet alreadyTraversed){ prereqChain.add(this); try { IClasspathEntry[] classpath = preferredClasspath == null ? getResolvedClasspath(true) : preferredClasspath; for (int i = 0, length = classpath.length; i < length; i++) { IClasspathEntry entry = classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { IPath entryPath = entry.getPath(); IResource member = workspaceRoot.findMember (entryPath); if (member != null && member.getType() == IResource.PROJECT){ JavaProject project = (JavaProject) JavaCore.create((IProject)member); int index = cycleParticipants.contains (project) ? 0 : prereqChain.indexOf(project); if (index >= 0) { // refer to cycle, or in cycle itself for (int size = prereqChain.size (); index < size; index++) { cycleParticipants.add (prereqChain.get(index)); } } else { if (!alreadyTraversed.contains (project)) { project.updateCycleParticipants(null, prereqChain, cycleParticipants, workspaceRoot, alreadyTraversed); } } } } } } catch(JavaModelException e){ } prereqChain.remove(this); alreadyTraversed.add(this); }
Created attachment 4518 [details] Proposed implementation as an attachment
Released slightly better version, which doesn't create handle, but rather accumulate paths. Fixed. Will also backport to 2.1 maintenance stream, and post a patch on JDT/Core update area shortly (http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout% 7E/jdt-core-home/r2.1/main.html#updates).
Backported to 2.1 maintenance stream.
Also IJavaProject#hasClasspathCycle suffered from a similar performance issue. Fixed by using the new updateCycleParticipants implementation instead. Backported this extra fix too.
Fixed in 2.2 stream as well.
Verified.
Verified for 3.0M1.