Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 327717

Summary: ConcurrentModificationException in JavaModelManager.initializeAllContainers
Product: [Eclipse Project] JDT Reporter: Kevan Holdaway <kholdaway>
Component: CoreAssignee: Andrey Loskutov <loskutov>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: jarthana, loskutov, mauromol, morrisj, Olivier_Thomann, shr31223, udo.walker, Vikas.Chandra
Version: 3.6.1   
Target Milestone: 4.8 M7   
Hardware: All   
OS: All   
See Also: https://git.eclipse.org/r/119187
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=ee652a24af3d74f53b78df6582d6a7a6fb8fdefc
Whiteboard:
Attachments:
Description Flags
Error Logs of problem none

Description Kevan Holdaway CLA 2010-10-13 18:40:18 EDT
Build Identifier: M20100909-0800

While JDT is initializing, other threads requesting information can cause a java.util.ConcurrentModificationException due to concurrent access to a HashMap in JavaModelManager.

See these exceptions:
java.util.ConcurrentModificationException
	at java.util.HashMap$AbstractMapIterator.checkConcurrentMod(Unknown Source)
	at java.util.HashMap$AbstractMapIterator.makeNext(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager$11.run(Unknown Source)
	at org.eclipse.core.internal.resources.Workspace.run(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager.initializeAllContainers(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager.getClasspathContainer(Unknown Source)
	at org.eclipse.jdt.core.JavaCore.initializeAfterLoad(Unknown Source)
	at org.eclipse.jdt.internal.ui.InitializeAfterLoadJob$RealJob.run(Unknown Source)
	at org.eclipse.core.internal.jobs.Worker.run(Unknown Source)

java.util.ConcurrentModificationException
	at java.util.HashMap$AbstractMapIterator.checkConcurrentMod(Unknown Source)
	at java.util.HashMap$AbstractMapIterator.makeNext(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at java.util.HashMap$EntryIterator.next(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager$11.run(Unknown Source)
	at org.eclipse.core.internal.resources.Workspace.run(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager.initializeAllContainers(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaModelManager.getClasspathContainer(Unknown Source)
	at org.eclipse.jdt.core.JavaCore.getClasspathContainer(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaProject.resolveClasspath(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaProject.resolveClasspath(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaProject.getResolvedClasspath(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaProject.buildStructure(Unknown Source)
	at org.eclipse.jdt.internal.core.Openable.generateInfos(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaElement.getChildren(Unknown Source)
	at org.eclipse.jdt.internal.core.JavaProject.getPackageFragmentRoots(Unknown Source)
	at reallyCoolPropertyTester.CoolPropertyTester.test(Unknown Source)



The property tester is from a product that extends eclipse.  


Reproducible: Sometimes

Steps to Reproduce:
1.  Start in a new workspace and create a couple web projects.
2.  Select File->Switch Workspace->Other
3.  Point to another existing workspace with lots of projects (in my case the workspace was also from an earlier version of eclipse (Version: 3.6.0.v20100525-7b7mFKtFEx2XmfZ4_B7NUJA, Build id: I20100608-0911)
4.  A property tester from a background job triggers this.
Comment 1 Olivier Thomann CLA 2010-10-13 20:20:27 EDT
Jay, please investigate.
Comment 2 Jay Arthanareeswaran CLA 2010-10-14 07:49:50 EDT
Is there a chance you can provide attach the log file when the error occurs, preferably with debug on for the following?

org.eclipse.jdt.core/debug/cpresolution
org.eclipse.jdt.core/debug/cpresolution/advanced
org.eclipse.jdt.core/debug/cpresolution/failure

Thanks!
Comment 3 Kevan Holdaway CLA 2010-10-14 10:45:37 EDT
Created attachment 180884 [details]
Error Logs of problem
Comment 4 Kevan Holdaway CLA 2010-10-14 10:47:26 EDT
(In reply to comment #2)
> Is there a chance you can provide attach the log file when the error occurs,
> preferably with debug on for the following?
> 
> org.eclipse.jdt.core/debug/cpresolution
> org.eclipse.jdt.core/debug/cpresolution/advanced
> org.eclipse.jdt.core/debug/cpresolution/failure
> 
> Thanks!

You can see for the two exceptions in the logs that 2 threads are inside of the method org.eclipse.jdt.internal.core.JavaModelManager$11.run(Unknown Source) and both of them are modifying the same hashmap.  That should be enough to determine there is no thread safety protection on that method.
Comment 5 Kevan Holdaway CLA 2010-10-15 10:47:46 EDT
Increasing severity to match the severity of the bug in our product.  Basically, this seems to have a wide impact on JDT operations when this occurs.  It requires a restart of the product to fix it.
Comment 6 Jay Arthanareeswaran CLA 2010-10-18 04:15:11 EDT
(In reply to comment #4)
> You can see for the two exceptions in the logs that 2 threads are inside of the
> method org.eclipse.jdt.internal.core.JavaModelManager$11.run(Unknown Source)
> and both of them are modifying the same hashmap.  That should be enough to
> determine there is no thread safety protection on that method.

Assuming that the internal maps contained by JavaModelManager#containersBeingInitialized is the ones that are affected here - containersBeingInitialized itself is in ThreadLocal- under normal circumstances these maps will be modified while being iterated in the code after line no 2694 (after comment // Set all containers).

So, I think there is another thread involved here, which has modified the map in question. So, I was hoping to find some clues from the log file that would lead us to the exact scenario that led to this. Will see what we can do here without affecting the performance much.
Comment 7 Jay Arthanareeswaran CLA 2010-10-18 06:37:57 EDT
(In reply to comment #6)
> So, I think there is another thread involved here, which has modified the map
> in question. So, I was hoping to find some clues from the log file that would
> lead us to the exact scenario that led to this. Will see what we can do here
> without affecting the performance much.

Oops! Sorry, having mentioned the ThreadLocal, the chances of an involvement from another thread is almost ruled out. Will take a close look at the stack trace and see what the possibilities of the map being modified in the same thread.
Comment 8 Kevan Holdaway CLA 2010-10-18 08:52:28 EDT
(In reply to comment #7)
> (In reply to comment #6)
> > So, I think there is another thread involved here, which has modified the map
> > in question. So, I was hoping to find some clues from the log file that would
> > lead us to the exact scenario that led to this. Will see what we can do here
> > without affecting the performance much.
> 
> Oops! Sorry, having mentioned the ThreadLocal, the chances of an involvement
> from another thread is almost ruled out. Will take a close look at the stack
> trace and see what the possibilities of the map being modified in the same
> thread.

I don't think that's true.  Notice is the Iterator.next() that is causing you the issue.  You only use ThreadLocal to acquire the map, but not to iterate it.  ThreadLocal is not a lock and doesn't create a synchronization block.  Its possible that 2 threads get the map using ThreadLocal then proceed and both start iterating.
Comment 9 Jay Arthanareeswaran CLA 2010-10-18 23:42:01 EDT
(In reply to comment #8)
> I don't think that's true.  Notice is the Iterator.next() that is causing you
> the issue.  You only use ThreadLocal to acquire the map, but not to iterate it.
>  ThreadLocal is not a lock and doesn't create a synchronization block.  Its
> possible that 2 threads get the map using ThreadLocal then proceed and both
> start iterating.

This is what I meant: Since we are using ThreadLocal to store/retrieve the outer map itself, won't each thread get it's own copy of the map? I don't see this map or the internal map (containers for each project) being passed around, which should take care of the concurrency issue. Let me know if I have missed something.
Comment 10 Jay Arthanareeswaran CLA 2010-10-19 23:17:45 EDT
Kevan, I could really use the log file with the DEBUG options enabled. Theoretically there are not many scenarios when the map would be modified from the same thread. The log file, with container initialization and things like that could throw some light on this.
Comment 11 Kevan Holdaway CLA 2010-10-20 18:59:51 EDT
(In reply to comment #10)
> Kevan, I could really use the log file with the DEBUG options enabled.
> Theoretically there are not many scenarios when the map would be modified from
> the same thread. The log file, with container initialization and things like
> that could throw some light on this.

I will try to gather some more information (using the debug options).
Comment 12 Kevan Holdaway CLA 2010-10-20 19:31:31 EDT
(In reply to comment #2)
> Is there a chance you can provide attach the log file when the error occurs,
> preferably with debug on for the following?
> 
> org.eclipse.jdt.core/debug/cpresolution
> org.eclipse.jdt.core/debug/cpresolution/advanced
> org.eclipse.jdt.core/debug/cpresolution/failure
> 
> Thanks!

Can you translate these instruction into what I would need to put in the eclipse.ini file?  

At first I assumed it was just -Dorg.eclipse.jdt.core/debug/cpresolution=true

but ... that didn't work.  How can I enable this without a source build?  Assume I can only launch eclipse and tweak it using args.
Comment 13 Olivier Thomann CLA 2010-10-20 20:09:05 EDT
You need to save the following contents in a file called ".option" that you put in the same folder of your eclipse.exe.

org.eclipse.jdt.core/debug=true
org.eclipse.jdt.core/debug/cpresolution/failure=true
org.eclipse.jdt.core/debug/cpresolution=true
org.eclipse.jdt.core/debug/cpresolution/advanced=true
Comment 14 Roberto Sanchez Herrera CLA 2011-02-25 15:55:07 EST
(In reply to comment #13)
> You need to save the following contents in a file called ".option" that you put
> in the same folder of your eclipse.exe.
> 
> org.eclipse.jdt.core/debug=true
> org.eclipse.jdt.core/debug/cpresolution/failure=true
> org.eclipse.jdt.core/debug/cpresolution=true
> org.eclipse.jdt.core/debug/cpresolution/advanced=true

I'm seeing a similar problem. I already created the .option (or it is .options?). What will I see with this options turned on? More information in the Eclipse log? or additional logs?
Comment 15 Jay Arthanareeswaran CLA 2011-02-27 22:31:54 EST
(In reply to comment #14)
> I'm seeing a similar problem. I already created the .option (or it is
> .options?). What will I see with this options turned on? More information in
> the Eclipse log? or additional logs?

It is .options and the eclipse log will tell us what's going on with the container initialization, whether there are any failures etc.
Comment 16 Andrey Loskutov CLA 2018-03-11 18:14:15 EDT
*** Bug 530765 has been marked as a duplicate of this bug. ***
Comment 17 Andrey Loskutov CLA 2018-03-11 18:27:21 EDT
*** Bug 367440 has been marked as a duplicate of this bug. ***
Comment 18 Andrey Loskutov CLA 2018-03-11 18:40:49 EDT
I see from Aeri that this is still a problem, https://dev.eclipse.org/recommenders/committers/aeri/v2/#!/problems/55c30ad5e4b0d6907d414f15

Source in initializeAllContainers() at commit 1114cb59520dae024355aa887ab8fe408024a596 (4.7.2) looks like:

Map perProjectContainers = (Map) JavaModelManager.this.containersBeingInitialized.get();
if (perProjectContainers != null) {
	Iterator entriesIterator = perProjectContainers.entrySet().iterator();
	while (entriesIterator.hasNext()) {
		Map.Entry entry = (Map.Entry) entriesIterator.next(); // error here, line 3033
		IJavaProject project = (IJavaProject) entry.getKey();
		HashMap perPathContainers = (HashMap) entry.getValue();
		Iterator containersIterator = perPathContainers.entrySet().iterator();
		while (containersIterator.hasNext()) {
			Map.Entry containerEntry = (Map.Entry) containersIterator.next(); // error here, line 3038
IPath containerPath = containerEntry.getKey();
			IClasspathContainer container = containerEntry.getValue();
			SetContainerOperation operation = new SetContainerOperation(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container});
			operation.runOperation(monitor); // root cause, line 3042

The errors are reported for both places at lines 3033 and line 3038 (entriesIterator.next() and containersIterator.next()).

containersBeingInitialized is a ThreadLocal, so how we can have a ConcurrentModificationException here? The fun is that we don't have *different* threads issue, we have the issue with the modifying maps *while iterating over them*. The line 3042 (operation.runOperation()) is the root cause. It invalidates the iterators by changing the underlined maps *in same* thread.
Comment 19 Eclipse Genie CLA 2018-03-11 19:25:16 EDT
New Gerrit change created: https://git.eclipse.org/r/119187
Comment 20 Andrey Loskutov CLA 2018-03-12 17:05:42 EDT
(In reply to Eclipse Genie from comment #19)
> New Gerrit change created: https://git.eclipse.org/r/119187

Jay, I think I have a fix.
Comment 22 Vikas Chandra CLA 2018-05-09 09:18:14 EDT
Verified by code inspection.