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