groovy
  1. groovy
  2. GROOVY-2961

ConcurrentModificationException in CachedClass when running same script in multiple threads

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Cannot Reproduce
    • Affects Version/s: 1.5.6
    • Fix Version/s: None
    • Component/s: class generator
    • Labels:
      None
    • Number of attachments :
      0

      Description

      If getInterfaces() is called from another thread, before CachedClass has been initialized, an exception maybe thrown. CachedClass should be put into the shared CACHED_CLASS_MAP after it has been initialized and not before. The initialize block may need to be synchronized to prevent a possible race condition.

      java.util.ConcurrentModificationException
              at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
              at java.util.HashMap$KeyIterator.next(HashMap.java:828)
              at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
              at org.codehaus.groovy.reflection.CachedClass.getInterfaces(CachedClass.java:70)
              at org.codehaus.groovy.reflection.CachedClass.initialize(CachedClass.java:134)
              at org.codehaus.groovy.reflection.ReflectionCache.getCachedClass(ReflectionCache.java:276)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getGlobalMetaClass(MetaClassRegistryImpl.java:250)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.access$100(MetaClassRegistryImpl.java:45)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getFromGlobal(MetaClassRegistryImpl.java:112)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getMetaClass(MetaClassRegistryImpl.java:88)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$MyThreadLocal.getMetaClass(MetaClassRegistryImpl.java:361)
              at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:265)
              at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:178)
              at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:477)
      

      — CachedClass.java:70 —

          private Set interfaces;
      
          public Set getInterfaces() {
              if (interfaces == null)  {
                  interfaces = new HashSet (0);
      
                  if (getCachedClass().isInterface())
                    interfaces.add(this);
      
                  Class[] classes = getCachedClass().getInterfaces();
                  for (int i = 0; i < classes.length; i++) {
                      final CachedClass aClass = ReflectionCache.getCachedClass(classes[i]);
                      if (!interfaces.contains(aClass))
                        interfaces.addAll(aClass.getInterfaces());
                  }
      
                  final CachedClass superClass = getCachedSuperClass();
                  if (superClass != null)
                    interfaces.addAll(superClass.getInterfaces());
              }
              return interfaces;
          }
      

      — ReflectionCache.java:276 —

                  CachedClass fasterCachedClass = null;
      
                  // Double-check put.
                  synchronized (CACHED_CLASS_MAP) {
                      ref = (SoftReference) CACHED_CLASS_MAP.get(klazz);
      
                      if (ref == null || (fasterCachedClass = (CachedClass) ref.get()) == null) {
                          CACHED_CLASS_MAP.put(klazz, new SoftReference(cachedClass));
                      } else {
                          // We must use the one that there first, we should be able to safely toss the one we made.
                          // By locking Class we would eliminate this race, but until the design is corrected we risk
                          // deadlock.
                          cachedClass = fasterCachedClass;
                      }
                  }
      
                  if (null == fasterCachedClass) {
                      // We've got a new CacheClass, now get loaded into the assignableMap.
                  	cachedClass.initialize();
                  }
      

        Activity

        Hide
        Chris Braun added a comment -

        I have seen this on 1.5.6 when I have two classes that extend the same class and call newInstance() on each of the classes concurrently.

        HitachiAccessHandleCollector extends HitachiDiscoveryCollector extends BaseCollector
        Thread1 - HitachiAccessHandleCollector.newInstance
        Thread2 - HitachiDiscoveryCollector.newInstance

        Results in race condition and the stack trace as follows:

        2010-08-28 08:30:00,018 ERROR collector-14 [com.akorri.bp.collectionserver.exception.DCSSException] Collector instantiation failed -> class com.akorri.bp.collectionserver.collectors.HitachiAccessHandleCollector
        java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
        at java.util.HashMap$KeyIterator.next(HashMap.java:828)
        at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
        at org.codehaus.groovy.reflection.CachedClass.getInterfaces(CachedClass.java:75)
        at org.codehaus.groovy.reflection.CachedClass.initialize(CachedClass.java:134)
        at org.codehaus.groovy.reflection.ReflectionCache.getCachedClass(ReflectionCache.java:276)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getGlobalMetaClass(MetaClassRegistryImpl.java:250)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.access$100(MetaClassRegistryImpl.java:45)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getFromGlobal(MetaClassRegistryImpl.java:112)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getMetaClass(MetaClassRegistryImpl.java:88)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$MyThreadLocal.getMetaClass(MetaClassRegistryImpl.java:361)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:265)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:729)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.initMetaClass(ScriptBytecodeAdapter.java:799)
        at sun.reflect.GeneratedMethodAccessor137.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
        at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
        at org.codehaus.groovy.runtime.InvokerHelper.invokeStaticMethod(InvokerHelper.java:804)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeStaticMethodN(ScriptBytecodeAdapter.java:215)
        at com.akorri.bp.collectionserver.collectors.BaseCollector.(BaseCollector.groovy:100)
        at com.akorri.bp.collectionserver.collectors.HitachiDiscoveryCollector.(HitachiDiscoveryCollector.groovy)
        at com.akorri.bp.collectionserver.collectors.HitachiAccessHandleCollector.(HitachiAccessHandleCollector.groovy)
        at sun.reflect.GeneratedConstructorAccessor329.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at com.akorri.bp.collectionserver.collector.DcssService.collectNow(DcssService.java:281)

        Show
        Chris Braun added a comment - I have seen this on 1.5.6 when I have two classes that extend the same class and call newInstance() on each of the classes concurrently. HitachiAccessHandleCollector extends HitachiDiscoveryCollector extends BaseCollector Thread1 - HitachiAccessHandleCollector.newInstance Thread2 - HitachiDiscoveryCollector.newInstance Results in race condition and the stack trace as follows: 2010-08-28 08:30:00,018 ERROR collector-14 [com.akorri.bp.collectionserver.exception.DCSSException] Collector instantiation failed -> class com.akorri.bp.collectionserver.collectors.HitachiAccessHandleCollector java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$KeyIterator.next(HashMap.java:828) at java.util.AbstractCollection.addAll(AbstractCollection.java:305) at org.codehaus.groovy.reflection.CachedClass.getInterfaces(CachedClass.java:75) at org.codehaus.groovy.reflection.CachedClass.initialize(CachedClass.java:134) at org.codehaus.groovy.reflection.ReflectionCache.getCachedClass(ReflectionCache.java:276) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getGlobalMetaClass(MetaClassRegistryImpl.java:250) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.access$100(MetaClassRegistryImpl.java:45) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getFromGlobal(MetaClassRegistryImpl.java:112) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$LocallyKnownClasses.getMetaClass(MetaClassRegistryImpl.java:88) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl$MyThreadLocal.getMetaClass(MetaClassRegistryImpl.java:361) at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:265) at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:729) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.initMetaClass(ScriptBytecodeAdapter.java:799) at sun.reflect.GeneratedMethodAccessor137.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230) at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105) at org.codehaus.groovy.runtime.InvokerHelper.invokeStaticMethod(InvokerHelper.java:804) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeStaticMethodN(ScriptBytecodeAdapter.java:215) at com.akorri.bp.collectionserver.collectors.BaseCollector.(BaseCollector.groovy:100) at com.akorri.bp.collectionserver.collectors.HitachiDiscoveryCollector.(HitachiDiscoveryCollector.groovy) at com.akorri.bp.collectionserver.collectors.HitachiAccessHandleCollector.(HitachiAccessHandleCollector.groovy) at sun.reflect.GeneratedConstructorAccessor329.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at java.lang.Class.newInstance0(Class.java:355) at java.lang.Class.newInstance(Class.java:308) at com.akorri.bp.collectionserver.collector.DcssService.collectNow(DcssService.java:281)
        Hide
        Guillaume Laforge added a comment -

        The code base has changed since back then, and we've not been able to reproduce the problem.
        Please open a new issue with a reproducable case, if ever you still encounter that issue.

        Show
        Guillaume Laforge added a comment - The code base has changed since back then, and we've not been able to reproduce the problem. Please open a new issue with a reproducable case, if ever you still encounter that issue.

          People

          • Assignee:
            Guillaume Laforge
            Reporter:
            James Leigh
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: