Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 335135 - IndexOfBoundsException during unload
Summary: IndexOfBoundsException during unload
Status: VERIFIED FIXED
Alias: None
Product: MDT.UML2
Classification: Modeling
Component: Core (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: UML2 Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-01-23 15:52 EST by Rafael Chaves CLA
Modified: 2012-09-10 09:07 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rafael Chaves CLA 2011-01-23 15:52:48 EST
I am seeing different failures during resource unloading deep in CacheAdapter code. Sometimes it is an infinite loop. Sometimes a NPE. Sometimes a IOBE. Sometimes a memory leak. That leads me to believe there is a thread safety issue in CacheAdapter.

My code for unloading a resource set is as follows:

public static void unloadResources(ResourceSet resourceSet) {
  for (Resource resource : resourceSet.getResources())
    current.unload();
}

The first resources are always:

pathmap://UML_METAMODELS/UML.metamodel.uml
pathmap://UML_PROFILES/Ecore.profile.uml
pathmap://UML_PROFILES/Standard.profile.uml
pathmap://UML_METAMODELS/Ecore.metamodel.uml
pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml

The rest is made of my own profiles, models etc.

My test case builds a model, a profile and applies the profile to it. After persisting the model, it unloads all resources. It does that in several threads at the same time, each of their own resource set. 

At this point I don't have a standalone test case that would allow you to easily reproduce it. I hope the stack trace I will include soon can give you an idea of what the problem can be.
Comment 1 Rafael Chaves CLA 2011-01-23 15:53:14 EST
Exception in thread "Thread-1" org.eclipse.emf.common.util.BasicEList$BasicIndexOutOfBoundsException: index=20, size=20
	at org.eclipse.emf.common.util.BasicEList.remove(BasicEList.java:608)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter$InverseCrossReferencer.remove(ECrossReferenceAdapter.java:216)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:765)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.AbstractEList.didClear(AbstractEList.java:172)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.clear(ArrayDelegatingEList.java:705)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.unloaded(ResourceImpl.java:1560)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.doUnload(ResourceImpl.java:1619)
	at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doUnload(XMLResourceImpl.java:506)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.unload(ResourceImpl.java:1634)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResource(MDDUtil.java:464)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResources(MDDUtil.java:460)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResources(MDDUtil.java:455)
	at com.abstratt.mdd.internal.core.Repository.dispose(Repository.java:302)
	at com.abstratt.mdd.core.tests.LeakTest$1.run(LeakTest.java:152)
	at java.lang.Thread.run(Unknown Source)
Comment 2 Rafael Chaves CLA 2011-01-23 16:01:47 EST
The NPE case:

Exception in thread "Thread-18" java.lang.NullPointerException
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter$InverseCrossReferencer.remove(ECrossReferenceAdapter.java:208)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:765)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.remove(ArrayDelegatingEList.java:656)
	at org.eclipse.emf.common.util.AbstractEList.remove(AbstractEList.java:466)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.removeAdapter(ECrossReferenceAdapter.java:825)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:781)
	at org.eclipse.uml2.common.util.CacheAdapter.unsetTarget(CacheAdapter.java:325)
	at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.unsetTarget(ECrossReferenceAdapter.java:744)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:510)
	at org.eclipse.emf.ecore.impl.MinimalEObjectImpl$1.didRemove(MinimalEObjectImpl.java:1)
	at org.eclipse.emf.common.util.AbstractEList.didClear(AbstractEList.java:172)
	at org.eclipse.emf.common.util.ArrayDelegatingEList.clear(ArrayDelegatingEList.java:705)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.unloaded(ResourceImpl.java:1560)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.doUnload(ResourceImpl.java:1619)
	at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doUnload(XMLResourceImpl.java:506)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.unload(ResourceImpl.java:1634)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResource(MDDUtil.java:464)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResources(MDDUtil.java:460)
	at com.abstratt.mdd.core.util.MDDUtil.unloadResources(MDDUtil.java:455)
	at com.abstratt.mdd.internal.core.Repository.dispose(Repository.java:302)
	at com.abstratt.mdd.core.tests.LeakTest$1.run(LeakTest.java:152)
	at java.lang.Thread.run(Unknown Source)
Comment 3 Rafael Chaves CLA 2011-01-23 17:37:07 EST
Just found this in CacheAdapter.selfAdapt:

		Object notifier = notification.getNotifier();

		if (notifier instanceof Resource
			&& notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {

			if (notification.getNewBooleanValue()) {
				unloadedResources.remove(notifier);

				for (Notifier child : ((Resource) notifier).getContents()) {
					addAdapter(child);
				}
			} else {
				unloadedResources.add((Resource) notifier);



Note than unloadedResources is modified/read without any synchronization. That means two threads unloading resources at the same time may cause data corruption on the unloadedResources set.

Not sure this is causing the issues I am seeing, maybe a different issue, but is looks fishy nevertheless.

To be frank, the fact that CacheAdapter is a singleton and the underlying ECrossReferenceAdapter does not seem to be meant to be thread-safe makes me think the whole global CacheAdapter approach to be unworkable for any multi-threaded application. 

I don't understand the code well enough, so I may be wrong and the issue may be fixable.

I did look into replacing cache adapter with a different implementation (that I would reimplement to keep data per thread - using ThreadLocal, for instance) but some of the data is kept at the level of ECrossReferenceAdapter so I wouldn't be able to ensure that data is accessed in a thread safe way.
Comment 4 Kenn Hussey CLA 2011-01-27 20:17:46 EST
Do the changes attached to bug 335125 address this issue? See also bug 332057 for what appears to be a related issue (and workaround).
Comment 5 Rafael Chaves CLA 2012-05-13 09:46:02 EDT
Any chance this is making it to Juno? I have been running with a modified version of UML2 for a year and a half now just because of this issue. It would be great if I could just use UML2 as is.

From my point of view, these changes are pretty safe and small and address a clear issue.
Comment 6 Kenn Hussey CLA 2012-05-13 19:55:17 EDT
(In reply to comment #5)
> Any chance this is making it to Juno? I have been running with a modified
> version of UML2 for a year and a half now just because of this issue. It would
> be great if I could just use UML2 as is.

OK, so the patch attached to bug 335125 fixes this issue for you?
Comment 7 Kenn Hussey CLA 2012-05-13 20:39:02 EDT
Marking this bug as fixed now that changes for bug 335125 are available in an integration build. If you think there's something more that can be done in UML2 for this issue, please feel free to re-open it.
Comment 8 Rafael Chaves CLA 2012-09-08 22:36:10 EDT
Sorry, wasn't getting emails from Bugzilla, yes, never seen this again.