You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/09 12:23:08 UTC

groovy git commit: Minor refactoring: classCache and sourceCache

Repository: groovy
Updated Branches:
  refs/heads/master 37d08fb1f -> 2c0a3bf9c


Minor refactoring: classCache and sourceCache


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/2c0a3bf9
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/2c0a3bf9
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/2c0a3bf9

Branch: refs/heads/master
Commit: 2c0a3bf9c712d91237a2a45d01c3135127cab212
Parents: 37d08fb
Author: sunlan <su...@apache.org>
Authored: Sat Dec 9 20:22:57 2017 +0800
Committer: sunlan <su...@apache.org>
Committed: Sat Dec 9 20:22:57 2017 +0800

----------------------------------------------------------------------
 src/main/groovy/lang/GroovyClassLoader.java     | 111 +++-----------
 .../runtime/memoize/EvictableMemoizeCache.java  |  68 +++++++++
 .../groovy/runtime/memoize/SimpleCache.java     | 152 +++++++++++++++++++
 3 files changed, 242 insertions(+), 89 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/2c0a3bf9/src/main/groovy/lang/GroovyClassLoader.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/GroovyClassLoader.java b/src/main/groovy/lang/GroovyClassLoader.java
index 9752f87..601a494 100644
--- a/src/main/groovy/lang/GroovyClassLoader.java
+++ b/src/main/groovy/lang/GroovyClassLoader.java
@@ -43,6 +43,8 @@ import org.codehaus.groovy.control.Phases;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.runtime.IOGroovyMethods;
 import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.memoize.EvictableMemoizeCache;
+import org.codehaus.groovy.runtime.memoize.SimpleCache;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Opcodes;
@@ -70,9 +72,6 @@ import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * A ClassLoader which can load Groovy classes. The loaded classes are cached,
@@ -96,19 +95,13 @@ public class GroovyClassLoader extends URLClassLoader {
     /**
      * this cache contains the loaded classes or PARSING, if the class is currently parsed
      */
-    protected final Map<String, Class> classCache = new HashMap<String, Class>(); // TODO should we make classCache private?
-    private final ReentrantReadWriteLock rwlForClassCache = new ReentrantReadWriteLock();
-    private final ReentrantReadWriteLock.ReadLock readLockForClassCache = rwlForClassCache.readLock();
-    private final ReentrantReadWriteLock.WriteLock writeLockForClassCache = rwlForClassCache.writeLock();
+    protected final SimpleCache<String, Class> classCache = new SimpleCache<String, Class>();
 
     /**
      * This cache contains mappings of file name to class. It is used
      * to bypass compilation.
      */
-    protected final Map<String, Class> sourceCache = new HashMap<String, Class>();
-    private final ReentrantReadWriteLock rwlForSourceCache = new ReentrantReadWriteLock();
-    private final ReentrantReadWriteLock.ReadLock readLockForSourceCache = rwlForSourceCache.readLock();
-    private final ReentrantReadWriteLock.WriteLock writeLockForSourceCache = rwlForSourceCache.writeLock();
+    protected final SimpleCache<String, Class> sourceCache = new SimpleCache<String, Class>();
 
     private final CompilerConfiguration config;
     private String sourceEncoding;
@@ -325,31 +318,17 @@ public class GroovyClassLoader extends URLClassLoader {
      * @param shouldCacheSource if true then the generated class will be stored in the source cache
      * @return the main class defined in the given script
      */
-    public Class parseClass(GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException {
-        Class answer;
-        String codeSourceName = codeSource.getName();
-
-        readLockForSourceCache.lock();
-        try {
-            answer = sourceCache.get(codeSourceName);
-            if (answer != null) return answer;
-        } finally {
-            readLockForSourceCache.unlock();
-        }
-
-        writeLockForSourceCache.lock();
-        try {
-            // try to find the cached class again
-            answer = sourceCache.get(codeSourceName);
-            if (answer != null) return answer;
-
-            answer = doParseClass(codeSource);
-            if (shouldCacheSource) sourceCache.put(codeSource.getName(), answer);
-        } finally {
-            writeLockForSourceCache.unlock();
-        }
-
-        return answer;
+    public Class parseClass(final GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException {
+        return sourceCache.getAndPut(
+                codeSource.getName(),
+                new EvictableMemoizeCache.ValueProvider<String, Class>() {
+                    @Override
+                    public Class provide(String key) {
+                        return doParseClass(codeSource);
+                    }
+                },
+                shouldCacheSource
+        );
     }
 
     private Class doParseClass(GroovyCodeSource codeSource) {
@@ -638,14 +617,7 @@ public class GroovyClassLoader extends URLClassLoader {
      * @see #clearCache()
      */
     protected Class getClassCacheEntry(String name) {
-        if (name == null) return null;
-
-        readLockForClassCache.lock();
-        try {
-            return classCache.get(name);
-        } finally {
-            readLockForClassCache.unlock();
-        }
+        return classCache.get(name);
     }
 
     /**
@@ -657,14 +629,7 @@ public class GroovyClassLoader extends URLClassLoader {
      * @see #clearCache()
      */
     protected void setClassCacheEntry(Class cls) {
-        String className = cls.getName();
-
-        writeLockForClassCache.lock();
-        try {
-            classCache.put(className, cls);
-        } finally {
-            writeLockForClassCache.unlock();
-        }
+        classCache.put(cls.getName(), cls);
     }
 
     /**
@@ -676,12 +641,7 @@ public class GroovyClassLoader extends URLClassLoader {
      * @see #clearCache()
      */
     protected void removeClassCacheEntry(String name) {
-        writeLockForClassCache.lock();
-        try {
-            classCache.remove(name);
-        } finally {
-            writeLockForClassCache.unlock();
-        }
+        classCache.remove(name);
     }
 
     /**
@@ -848,12 +808,7 @@ public class GroovyClassLoader extends URLClassLoader {
             if ((oldClass != null && isSourceNewer(source, oldClass)) || (oldClass == null)) {
                 String name = source.toExternalForm();
 
-                writeLockForSourceCache.lock();
-                try {
-                    sourceCache.remove(name);
-                } finally {
-                    writeLockForSourceCache.unlock();
-                }
+                sourceCache.remove(name);
 
                 if (isFile(source)) {
                     try {
@@ -1052,16 +1007,7 @@ public class GroovyClassLoader extends URLClassLoader {
      * @return all classes loaded by this class loader
      */
     public Class[] getLoadedClasses() {
-        final Collection<Class> values;
-
-        readLockForClassCache.lock();
-        try {
-            values = classCache.values();
-        } finally {
-            readLockForClassCache.unlock();
-        }
-
-        return values.toArray(EMPTY_CLASS_ARRAY);
+        return classCache.values().toArray(EMPTY_CLASS_ARRAY);
     }
 
     /**
@@ -1076,22 +1022,9 @@ public class GroovyClassLoader extends URLClassLoader {
      * @see #removeClassCacheEntry(String)
      */
     public void clearCache() {
-        Collection<Class> classesToClear;
+        Collection<Class> classesToClear = classCache.clear();
 
-        writeLockForClassCache.lock();
-        try {
-            classesToClear = classCache.values();
-            classCache.clear();
-        } finally {
-            writeLockForClassCache.unlock();
-        }
-
-        writeLockForSourceCache.lock();
-        try {
-            sourceCache.clear();
-        } finally {
-            writeLockForSourceCache.unlock();
-        }
+        sourceCache.clear();
 
         for (Class c : classesToClear) {
             // Another Thread may be using an instance of this class

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c0a3bf9/src/main/org/codehaus/groovy/runtime/memoize/EvictableMemoizeCache.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/memoize/EvictableMemoizeCache.java b/src/main/org/codehaus/groovy/runtime/memoize/EvictableMemoizeCache.java
new file mode 100644
index 0000000..83b3498
--- /dev/null
+++ b/src/main/org/codehaus/groovy/runtime/memoize/EvictableMemoizeCache.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.runtime.memoize;
+
+import java.util.Collection;
+
+/**
+ * Represents an evictable memoize cache with its essential methods
+ * @param <K> type of the keys
+ * @param <V> type of the values
+ */
+public interface EvictableMemoizeCache<K, V> extends MemoizeCache<K, V> {
+    /**
+     * Remove the cached value by the key
+     * @param key
+     * @return returns false if there was no matching key
+     */
+    boolean remove(K key);
+
+    /**
+     * Clear the cache
+     * @return returns cleared values
+     */
+    Collection<V> clear();
+
+    /**
+     * Try to get the value from cache.
+     * If not found, create the value by {@link ValueProvider} and put it into the cache, at last return the value
+     * @param key
+     * @return the cached value
+     */
+    V getAndPut(K key, ValueProvider<K, V> valueProvider);
+
+    /**
+     * Get all cached values
+     * @return all cached values
+     */
+    Collection<V> values();
+
+    /**
+     * Represents a provider used to create value
+     * @param <K> type of the key
+     * @param <V> type of the value
+     */
+    interface ValueProvider<K, V> {
+        /**
+         * Provide the created value
+         * @return
+         */
+        V provide(K key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c0a3bf9/src/main/org/codehaus/groovy/runtime/memoize/SimpleCache.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/memoize/SimpleCache.java b/src/main/org/codehaus/groovy/runtime/memoize/SimpleCache.java
new file mode 100644
index 0000000..50affef
--- /dev/null
+++ b/src/main/org/codehaus/groovy/runtime/memoize/SimpleCache.java
@@ -0,0 +1,152 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.runtime.memoize;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ *
+ * Represents a simple cache, which is thread safe and backed by {@link java.util.HashMap}
+ *
+ * @param <K> type of the keys
+ * @param <V> type of the values
+ */
+public class SimpleCache<K, V> implements EvictableMemoizeCache<K, V> {
+    private final Map<K, V> map = new HashMap<>();
+    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
+    private final ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
+
+    @Override
+    public V get(K key) {
+        if (null == key) {
+            return null;
+        }
+
+        readLock.lock();
+        try {
+            return map.get(key);
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    public V put(K key, V value) {
+        writeLock.lock();
+        try {
+            return map.put(key, value);
+        } finally {
+            writeLock.unlock();
+        }
+
+    }
+
+    @Override
+    public V getAndPut(K key, ValueProvider<K, V> valueProvider) {
+        return getAndPut(key, valueProvider, true);
+    }
+
+    public V getAndPut(K key, ValueProvider<K, V> valueProvider, boolean shouldCache) {
+        V value;
+
+        readLock.lock();
+        try {
+            value = map.get(key);
+            if (null != value) return value;
+        } finally {
+            readLock.unlock();
+        }
+
+        writeLock.lock();
+        try {
+            // try to find the cached value again
+            value = map.get(key);
+            if (null != value) return value;
+
+            value = valueProvider.provide(key);
+            if (shouldCache) map.put(key, value);
+        } finally {
+            writeLock.unlock();
+        }
+
+        return value;
+    }
+
+
+
+    @Override
+    public Collection<V> values() {
+        return map.values();
+    }
+
+    @Override
+    public boolean remove(K key) {
+        V removedValue;
+
+        writeLock.lock();
+        try {
+            removedValue = map.remove(key);
+        } finally {
+            writeLock.unlock();
+        }
+
+        return null != removedValue;
+    }
+
+    @Override
+    public Collection<V> clear() {
+        Collection<V> values;
+
+        writeLock.lock();
+        try {
+            values = map.values();
+            map.clear();
+        } finally {
+            writeLock.unlock();
+        }
+
+        return values;
+    }
+
+    @Override
+    public void cleanUpNullReferences() {
+        writeLock.lock();
+        try {
+            List<K> keys = new LinkedList<>();
+
+            for (Map.Entry<K, V> entry : map.entrySet()) {
+                if (null == entry.getValue()) {
+                    keys.add(entry.getKey());
+                }
+            }
+
+            for (K key : keys) {
+                map.remove(key);
+            }
+        } finally {
+            writeLock.unlock();
+        }
+    }
+}