This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 76c7708bcd GROOVY-9742: GroovyClassLoader.parseClass() 
StampedCommonCache.getAndPut() hang (provide constructor to enable alternative 
sourceCaches to be set)
76c7708bcd is described below

commit 76c7708bcd009ea4e5932a0777c7eb2c03169624
Author: Paul King <[email protected]>
AuthorDate: Sun Apr 13 13:05:10 2025 +1000

    GROOVY-9742: GroovyClassLoader.parseClass() StampedCommonCache.getAndPut() 
hang (provide constructor to enable alternative sourceCaches to be set)
---
 src/main/java/groovy/lang/GroovyClassLoader.java   | 19 +++++--
 .../bugs/groovy9742/CustomGroovyClassLoader.java   | 59 +++++-----------------
 ...oader.java => DelegatingGroovyClassLoader.java} |  6 +--
 src/test/groovy/bugs/groovy9742/Groovy9742.groovy  | 16 ++++--
 4 files changed, 42 insertions(+), 58 deletions(-)

diff --git a/src/main/java/groovy/lang/GroovyClassLoader.java 
b/src/main/java/groovy/lang/GroovyClassLoader.java
index 1c98269766..8fa0f5fc80 100644
--- a/src/main/java/groovy/lang/GroovyClassLoader.java
+++ b/src/main/java/groovy/lang/GroovyClassLoader.java
@@ -100,7 +100,7 @@ public class GroovyClassLoader extends URLClassLoader {
     /**
      * This cache contains mappings of file name to class. It is used to 
bypass compilation.
      */
-    protected final FlexibleEvictableCache<String, Class> sourceCache = new 
ConcurrentCommonCache<>();
+    protected final FlexibleEvictableCache<String, Class> sourceCache;
 
     private final CompilerConfiguration config;
     private final String sourceEncoding;
@@ -140,15 +140,28 @@ public class GroovyClassLoader extends URLClassLoader {
     }
 
     /**
-     * Creates a GroovyClassLoader.
+     * Creates a GroovyClassLoader with the default sourceCache.
      *
      * @param parent                    the parent class loader
      * @param config                    the compiler configuration
      * @param useConfigurationClasspath determines if the configurations 
classpath should be added
      */
     public GroovyClassLoader(final ClassLoader parent, final 
CompilerConfiguration config, final boolean useConfigurationClasspath) {
+        this(parent, config, useConfigurationClasspath, new 
ConcurrentCommonCache<>());
+    }
+
+    /**
+     * Creates a GroovyClassLoader with a custom sourceCache.
+     *
+     * @param parent                    the parent class loader
+     * @param config                    the compiler configuration
+     * @param useConfigurationClasspath determines if the configurations 
classpath should be added
+     * @param sourceCache               the source cache to use
+     */
+    public GroovyClassLoader(final ClassLoader parent, final 
CompilerConfiguration config, final boolean useConfigurationClasspath, 
FlexibleEvictableCache<String, Class> sourceCache) {
         super(EMPTY_URL_ARRAY, parent);
         this.config = (config != null ? config : 
CompilerConfiguration.DEFAULT);
+        this.sourceCache = sourceCache;
         if (useConfigurationClasspath) {
             for (String path : this.config.getClasspath()) {
                 addClasspath(path);
@@ -233,7 +246,7 @@ public class GroovyClassLoader extends URLClassLoader {
 
     private static void collect(final ClassCollector collector, final 
LinkedHashMap<ClassNode, ClassVisitor> generatedClasses) {
         // GROOVY-10687: drive ClassCollector after classgen -- interfaces 
first
-        var classes = new ArrayList<ClassNode>(generatedClasses.keySet());
+        var classes = new ArrayList<>(generatedClasses.keySet());
         classes.sort(Comparator.comparingInt(cn -> {
             int n;
             if (cn.isInterface()) {
diff --git a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java 
b/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
index 2bc87f7ab1..346941e650 100644
--- a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
+++ b/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
@@ -19,55 +19,20 @@
 package groovy.bugs.groovy9742;
 
 import groovy.lang.GroovyClassLoader;
-import java.io.File;
-import java.io.IOException;
-import java.net.URISyntaxException;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.runtime.memoize.StampedCommonCache;
 
-public class CustomGroovyClassLoader extends ClassLoader {
-    public int loadedCount = 0;
-    public CustomGroovyClassLoader(ClassLoader parent) {
-        super(parent);
-        groovyClassLoader = new GroovyClassLoader(this);
-    }
-    private static final File srcDir;
-
-    static {
-        try {
-            srcDir = new 
File(CustomGroovyClassLoader.class.getResource("/").toURI());
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e);
-        }
+/**
+ * A custom groovyClassLoader that users a StampedCommonCache as the 
sourceCache
+ */
+public class CustomGroovyClassLoader extends GroovyClassLoader {
+    public CustomGroovyClassLoader() {
+        super(Thread.currentThread().getContextClassLoader(), null, true, new 
StampedCommonCache<>());
     }
 
-    private final GroovyClassLoader groovyClassLoader;
-    @Override
-    protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
-        synchronized (getClassLoadingLock(name)) {
-            Class<?> c = doFindClass(name);
-            if (c != null) {
-                if (resolve) {
-                    resolveClass(c);
-                }
-                return c;
-            }
-        }
-        return super.loadClass(name, resolve);
-    }
-    private Class<?> doFindClass(String name) {
-        File classFile = new File(srcDir, name.replace('.', '/') + ".groovy");
-        if (classFile.exists()) {
-            try {
-//                System.out.println("PARSE\t: " + name);
-                Class<?> clz = groovyClassLoader.parseClass(classFile);
-                loadedCount++;
-//                System.out.println("PARSED\t: " + clz);
-                return clz;
-            }
-            catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        return null;
+    public Object evaluate(final String scriptText) {
+        GroovyClassLoader gcl = new CustomGroovyClassLoader();
+        GroovyShell gsh = new GroovyShell(gcl);
+        return gsh.evaluate(scriptText);
     }
 }
-
diff --git a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java 
b/src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
similarity index 91%
copy from src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
copy to src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
index 2bc87f7ab1..ef6bb9f380 100644
--- a/src/test/groovy/bugs/groovy9742/CustomGroovyClassLoader.java
+++ b/src/test/groovy/bugs/groovy9742/DelegatingGroovyClassLoader.java
@@ -23,9 +23,9 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
 
-public class CustomGroovyClassLoader extends ClassLoader {
+public class DelegatingGroovyClassLoader extends ClassLoader {
     public int loadedCount = 0;
-    public CustomGroovyClassLoader(ClassLoader parent) {
+    public DelegatingGroovyClassLoader(ClassLoader parent) {
         super(parent);
         groovyClassLoader = new GroovyClassLoader(this);
     }
@@ -33,7 +33,7 @@ public class CustomGroovyClassLoader extends ClassLoader {
 
     static {
         try {
-            srcDir = new 
File(CustomGroovyClassLoader.class.getResource("/").toURI());
+            srcDir = new 
File(DelegatingGroovyClassLoader.class.getResource("/").toURI());
         } catch (URISyntaxException e) {
             throw new RuntimeException(e);
         }
diff --git a/src/test/groovy/bugs/groovy9742/Groovy9742.groovy 
b/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
index 6747a0e6ec..e35d21dd6d 100644
--- a/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
+++ b/src/test/groovy/bugs/groovy9742/Groovy9742.groovy
@@ -31,13 +31,19 @@ class Groovy9742 {
     void testDeadLock() {
         ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
         Future future = fixedThreadPool.submit((Callable<Class<?>>) () -> {
-                CustomGroovyClassLoader ccl = new 
CustomGroovyClassLoader(Groovy9742.class.getClassLoader())
-                Class<?> clz = ccl.loadClass("groovy.bugs.groovy9742.Foo")
-                assert ccl.loadedCount == 2
-                return clz
-            })
+            DelegatingGroovyClassLoader ccl = new 
DelegatingGroovyClassLoader(Groovy9742.class.getClassLoader())
+            Class<?> clz = ccl.loadClass("groovy.bugs.groovy9742.Foo")
+            assert ccl.loadedCount == 2
+            return clz
+        })
         Class c = future.get(3000, TimeUnit.MILLISECONDS)
         assert c instanceof Class
         fixedThreadPool.shutdownNow()
     }
+
+    @Test
+    void testCustomGroovyClassLoader() {
+        CustomGroovyClassLoader gcl = new CustomGroovyClassLoader()
+        assert gcl.evaluate('1 + 1') == 2
+    }
 }

Reply via email to