Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-2961

ConcurrentModificationException in CachedClass when running same script in multiple threads

    Details

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

      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();
                  }
      

        Attachments

          Activity

            People

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

              Dates

              • Created:
                Updated:
                Resolved: