This is an automated email from the ASF dual-hosted git repository. sunlan pushed a commit to branch GROOVY-9631 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit fc931d62b40507e93f03abb33196784d640a7845 Author: Daniel Sun <[email protected]> AuthorDate: Fri Jul 10 01:25:05 2020 +0800 GROOVY-9631: Replace legacy data structure with Java collection --- .../groovy/reflection/GroovyClassValueFactory.java | 7 +- .../reflection/GroovyClassValuePreJava7.java | 106 ------- .../metaclass/ThreadManagedMetaBeanProperty.java | 2 +- .../groovy/util/AbstractConcurrentMap.java | 207 ------------- .../groovy/util/AbstractConcurrentMapBase.java | 336 --------------------- .../codehaus/groovy/util/ManagedConcurrentMap.java | 148 ++++----- .../util/AbstractConcurrentMapSegmentTest.groovy | 193 ------------ .../groovy/util/ManagedConcurrentMapTest.groovy | 2 - 8 files changed, 80 insertions(+), 921 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java index 0565f92..b761895 100644 --- a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java +++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java @@ -18,7 +18,6 @@ */ package org.codehaus.groovy.reflection; -import org.apache.groovy.util.SystemUtil; import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue; import org.codehaus.groovy.reflection.v7.GroovyClassValueJava7; @@ -29,11 +28,7 @@ class GroovyClassValueFactory { * See https://bugs.openjdk.java.net/browse/JDK-8136353 * This issue does not exist on IBM Java (J9) so use ClassValue by default on that JVM. */ - private static final boolean USE_CLASSVALUE = Boolean.parseBoolean(SystemUtil.getSystemPropertySafe("groovy.use.classvalue", "true")); - public static <T> GroovyClassValue<T> createGroovyClassValue(ComputeValue<T> computeValue) { - return (USE_CLASSVALUE) - ? new GroovyClassValueJava7<>(computeValue) - : new GroovyClassValuePreJava7<>(computeValue); + return new GroovyClassValueJava7<>(computeValue); } } diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java deleted file mode 100644 index 02f893c..0000000 --- a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.reflection; - -import org.codehaus.groovy.util.Finalizable; -import org.codehaus.groovy.util.ManagedConcurrentMap; -import org.codehaus.groovy.util.ReferenceBundle; - -/** Approximation of Java 7's {@link java.lang.ClassValue} that works on earlier versions of Java. - * Note that this implementation isn't as good at Java 7's; it doesn't allow for some GC'ing that Java 7 would allow. - * But, it's good enough for our use. - * - * @param <T> - */ -class GroovyClassValuePreJava7<T> implements GroovyClassValue<T> { - private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle(); - - private class EntryWithValue extends ManagedConcurrentMap.EntryWithValue<Class<?>,T>{ - - public EntryWithValue(GroovyClassValuePreJava7Segment segment, Class<?> key, int hash) { - super(weakBundle, segment, key, hash, computeValue.computeValue(key)); - } - - @Override - public void setValue(T value) { - if(value!=null) super.setValue(value); - } - - @Override - public void finalizeReference() { - T value = getValue(); - if (value instanceof Finalizable) { - ((Finalizable) value).finalizeReference(); - } - super.finalizeReference(); - } - } - - private class GroovyClassValuePreJava7Segment extends ManagedConcurrentMap.Segment<Class<?>,T> { - - private static final long serialVersionUID = 1289753977947029168L; - - GroovyClassValuePreJava7Segment(ReferenceBundle bundle, int initialCapacity) { - super(bundle, initialCapacity); - } - - @Override - protected EntryWithValue createEntry(Class<?> key, int hash, - T unused) { - return new EntryWithValue(this, key, hash); - } - } - - private class GroovyClassValuePreJava7Map extends ManagedConcurrentMap<Class<?>,T> { - - public GroovyClassValuePreJava7Map() { - super(weakBundle); - } - - @Override - protected GroovyClassValuePreJava7Segment createSegment(Object segmentInfo, int cap) { - ReferenceBundle bundle = (ReferenceBundle) segmentInfo; - if (bundle==null) throw new IllegalArgumentException("bundle must not be null "); - return new GroovyClassValuePreJava7Segment(bundle, cap); - } - - } - - private final ComputeValue<T> computeValue; - - private final GroovyClassValuePreJava7Map map = new GroovyClassValuePreJava7Map(); - - public GroovyClassValuePreJava7(ComputeValue<T> computeValue){ - this.computeValue = computeValue; - } - - @Override - public T get(Class<?> type) { - // the value isn't use in the getOrPut call - see the EntryWithValue constructor above - T value = ((EntryWithValue)map.getOrPut(type, null)).getValue(); - //all entries are guaranteed to be EntryWithValue. Value can only be null if computeValue returns null - return value; - } - - @Override - public void remove(Class<?> type) { - map.remove(type); - } - -} diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java index 5074908..b6ac4cf 100644 --- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java +++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java @@ -176,7 +176,7 @@ public class ThreadManagedMetaBeanProperty extends MetaBeanProperty { * @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[]) */ public Object invoke(Object object, Object[] arguments) { - return instance2Prop.getOrPut(object, getInitialValue()).getValue(); + return instance2Prop.computeIfAbsent(object, k -> getInitialValue()); } } diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java deleted file mode 100644 index 6168877..0000000 --- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.util; - -public abstract class AbstractConcurrentMap<K, V> extends AbstractConcurrentMapBase { - - public AbstractConcurrentMap(Object segmentInfo) { - super(segmentInfo); - } - - public Segment segmentFor (int hash) { - return (Segment) super.segmentFor(hash); - } - - public V get(K key) { - int hash = hash(key); - return (V) segmentFor(hash).get(key, hash); - } - - public Entry<K,V> getOrPut(K key, V value) { - int hash = hash(key); - return segmentFor(hash).getOrPut(key, hash, value); - } - - public void put(K key, V value) { - int hash = hash(key); - segmentFor(hash).put(key, hash, value); - } - - public void remove(K key) { - int hash = hash(key); - segmentFor(hash).remove(key, hash); - } - - public abstract static class Segment<K,V> extends AbstractConcurrentMapBase.Segment { - - private static final long serialVersionUID = -2392526467736920612L; - - protected Segment(int initialCapacity) { - super(initialCapacity); - } - - public final V get(K key, int hash) { - Object[] tab = table; - Object o = tab[hash & (tab.length - 1)]; - if (o != null) { - if (o instanceof Entry) { - Entry<K,V> e = (Entry) o; - if (e.isEqual(key, hash)) { - return e.getValue(); - } - } - else { - Object[] arr = (Object[]) o; - for (Object value : arr) { - Entry<K, V> e = (Entry<K, V>) value; - if (e != null && e.isEqual(key, hash)) { - return e.getValue(); - } - } - } - } - return null; - } - - public final Entry<K,V> getOrPut(K key, int hash, V value) { - Object[] tab = table; - Object o = tab[hash & (tab.length - 1)]; - if (o != null) { - if (o instanceof Entry) { - Entry<K,V> e = (Entry) o; - if (e.isEqual(key, hash)) { - return e; - } - } - else { - Object[] arr = (Object[]) o; - for (Object item : arr) { - Entry<K, V> e = (Entry<K, V>) item; - if (e != null && e.isEqual(key, hash)) { - return e; - } - } - } - } - return put(key, hash, value); - } - - public final Entry put(K key, int hash, V value) { - lock(); - try { - rehashIfThresholdExceeded(); - - Object[] tab = table; - int index = hash & (tab.length - 1); - Object o = tab[index]; - if (o != null) { - if (o instanceof Entry) { - Entry e = (Entry) o; - if (e.isEqual(key, hash)) { - e.setValue(value); - return e; - } - else { - Object[] arr = new Object [2]; - final Entry ee = createEntry(key, hash, value); - arr [0] = ee; - arr [1] = e; - tab[index] = arr; - count++; - return ee; - } - } - else { - Object[] arr = (Object[]) o; - for (Object item : arr) { - Entry e = (Entry) item; - if (e != null && e.isEqual(key, hash)) { - e.setValue(value); - return e; - } - } - - final Entry ee = createEntry(key, hash, value); - for (int i = 0; i < arr.length; i++) { - Entry e = (Entry) arr[i]; - if (e == null) { - arr [i] = ee; - count++; - return ee; - } - } - - Object[] newArr = new Object[arr.length+1]; - newArr [0] = ee; - System.arraycopy(arr, 0, newArr, 1, arr.length); - tab [index] = newArr; - count++; - return ee; - } - } - - Entry e = createEntry(key, hash, value); - tab[index] = e; - count++; // write-volatile - return e; - } finally { - unlock(); - } - } - - public void remove(K key, int hash) { - lock(); - try { - int c = count-1; - final Object[] tab = table; - final int index = hash & (tab.length - 1); - Object o = tab[index]; - - if (o != null) { - if (o instanceof Entry) { - if (((Entry<K,V>)o).isEqual(key, hash)) { - tab[index] = null; - count = c; - } - } - else { - Object[] arr = (Object[]) o; - for (int i = 0; i < arr.length; i++) { - Entry<K,V> e = (Entry<K,V>) arr[i]; - if (e != null && e.isEqual(key, hash)) { - arr [i] = null; - count = c; - break; - } - } - } - } - } - finally { - unlock(); - } - } - - protected abstract Entry<K,V> createEntry(K key, int hash, V value); - } - - public interface Entry<K, V> extends AbstractConcurrentMapBase.Entry<V>{ - boolean isEqual(K key, int hash); - } -} diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java deleted file mode 100644 index 01f84af..0000000 --- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * 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.util; - -import java.util.Collection; -import java.util.LinkedList; - -public abstract class AbstractConcurrentMapBase { - protected static final int MAXIMUM_CAPACITY = 1 << 30; - static final int MAX_SEGMENTS = 1 << 16; - static final int RETRIES_BEFORE_LOCK = 2; - final int segmentMask; - final int segmentShift; - protected final Segment[] segments; - - public AbstractConcurrentMapBase(Object segmentInfo) { - int sshift = 0; - int ssize = 1; - while (ssize < 16) { - ++sshift; - ssize <<= 1; - } - segmentShift = 32 - sshift; - segmentMask = ssize - 1; - this.segments = new Segment[ssize]; - - int c = 512 / ssize; - if (c * ssize < 512) - ++c; - int cap = 1; - while (cap < c) - cap <<= 1; - - for (int i = 0; i < this.segments.length; ++i) - this.segments[i] = createSegment(segmentInfo, cap); - } - - protected abstract Segment createSegment(Object segmentInfo, int cap); - - protected static <K> int hash(K key) { - int h = System.identityHashCode(key); - h += ~(h << 9); - h ^= (h >>> 14); - h += (h << 4); - h ^= (h >>> 10); - return h; - } - - public Segment segmentFor(int hash) { - return segments[(hash >>> segmentShift) & segmentMask]; - } - - public int fullSize() { - int count = 0; - for (Segment segment : segments) { - segment.lock(); - try { - for (int j = 0; j < segment.table.length; j++) { - Object o = segment.table[j]; - if (o != null) { - if (o instanceof Entry) { - count++; - } else { - Object[] arr = (Object[]) o; - count += arr.length; - } - } - } - } finally { - segment.unlock(); - } - } - return count; - } - - public int size() { - int count = 0; - for (Segment segment : segments) { - segment.lock(); - try { - for (int j = 0; j < segment.table.length; j++) { - Object o = segment.table[j]; - if (o != null) { - if (o instanceof Entry) { - Entry e = (Entry) o; - if (e.isValid()) - count++; - } else { - Object[] arr = (Object[]) o; - for (Object value : arr) { - Entry info = (Entry) value; - if (info != null && info.isValid()) - count++; - } - } - } - } - } finally { - segment.unlock(); - } - } - return count; - } - - public Collection values() { - Collection result = new LinkedList(); - for (Segment segment : segments) { - segment.lock(); - try { - for (int j = 0; j < segment.table.length; j++) { - Object o = segment.table[j]; - if (o != null) { - if (o instanceof Entry) { - Entry e = (Entry) o; - if (e.isValid()) - result.add(e); - } else { - Object[] arr = (Object[]) o; - for (Object value : arr) { - Entry info = (Entry) value; - if (info != null && info.isValid()) - result.add(info); - } - } - } - } - } finally { - segment.unlock(); - } - } - return result; - } - - public static class Segment extends LockableObject { - private static final long serialVersionUID = -4128828550135386431L; - volatile int count; - - int threshold; - - protected volatile Object[] table; - - protected Segment(int initialCapacity) { - setTable(new Object[initialCapacity]); - } - - void setTable(Object[] newTable) { - threshold = (int) (newTable.length * 0.75f); - table = newTable; - } - - void removeEntry (Entry e) { - lock (); - int newCount = count; - try { - Object [] tab = table; - int index = e.getHash() & (tab.length-1); - Object o = tab[index]; - if (o != null) { - if (o instanceof Entry) { - if (o == e) { - tab [index] = null; - newCount--; - } - } - else { - Object[] arr = (Object[]) o; - Object res = null; - for (Object value : arr) { - Entry info = (Entry) value; - if (info != null) { - if (info != e) { - if (info.isValid()) { - res = put(info, res); - } else { - newCount--; - } - } else { - newCount--; - } - } - } - tab [index] = res; - } - count = newCount; - } - } - finally { - unlock(); - } - } - - void rehashIfThresholdExceeded() { - if(count > threshold) { - rehash(); - } - } - - void rehash() { - Object[] oldTable = table; - int oldCapacity = oldTable.length; - if (oldCapacity >= MAXIMUM_CAPACITY) - return; - - int newCount = 0; - for (int i = 0; i < oldCapacity ; i++) { - Object o = oldTable [i]; - if (o != null) { - if (o instanceof Entry) { - Entry e = (Entry) o; - if (e.isValid()) { - newCount++; - } - else { - oldTable[i] = null; - } - } - else { - Object[] arr = (Object[]) o; - int localCount = 0; - for (int index = 0; index < arr.length; index++) { - Entry e = (Entry) arr[index]; - if (e != null && e.isValid()) { - localCount++; - } - else { - arr [index] = null; - } - } - if (localCount == 0) - oldTable[i] = null; - else - newCount += localCount; - } - } - } - - Object[] newTable = new Object[newCount+1 < threshold ? oldCapacity : oldCapacity << 1]; - int sizeMask = newTable.length - 1; - newCount = 0; - for (Object o : oldTable) { - if (o != null) { - if (o instanceof Entry) { - Entry e = (Entry) o; - if (e.isValid()) { - int index = e.getHash() & sizeMask; - put(e, index, newTable); - newCount++; - } - } else { - Object[] arr = (Object[]) o; - for (Object value : arr) { - Entry e = (Entry) value; - if (e != null && e.isValid()) { - int index = e.getHash() & sizeMask; - put(e, index, newTable); - newCount++; - } - } - } - } - } - - threshold = (int)(newTable.length * 0.75f); - - table = newTable; - count = newCount; - } - - private static void put(Entry ee, int index, Object[] tab) { - Object o = tab[index]; - if (o != null) { - if (o instanceof Entry) { - Object[] arr = new Object [2]; - arr [0] = ee; - arr [1] = o; - tab[index] = arr; - return; - } - else { - Object[] arr = (Object[]) o; - Object[] newArr = new Object[arr.length+1]; - newArr [0] = ee; - System.arraycopy(arr, 0, newArr, 1, arr.length); - tab [index] = newArr; - return; - } - } - tab[index] = ee; - } - - private static Object put(Entry ee, Object o) { - if (o != null) { - if (o instanceof Entry) { - Object[] arr = new Object [2]; - arr [0] = ee; - arr [1] = o; - return arr; - } - else { - Object[] arr = (Object[]) o; - Object[] newArr = new Object[arr.length+1]; - newArr [0] = ee; - System.arraycopy(arr, 0, newArr, 1, arr.length); - return newArr; - } - } - return ee; - } - } - - public interface Entry<V> { - V getValue(); - - void setValue(V value); - - int getHash(); - - boolean isValid(); - } -} diff --git a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java index d1cf760..89e4a21 100644 --- a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java +++ b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java @@ -18,102 +18,110 @@ */ package org.codehaus.groovy.util; -public class ManagedConcurrentMap<K,V> extends AbstractConcurrentMap<K,V> { - protected ReferenceBundle bundle; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ManagedConcurrentMap<K, V> { + private final ConcurrentMap<Key<K>, ManagedReference<V>> internalMap = new ConcurrentHashMap<>(); + private final ReferenceBundle bundle; + public ManagedConcurrentMap(ReferenceBundle bundle) { - super(bundle); + if (bundle == null) throw new IllegalArgumentException("bundle must not be null"); this.bundle = bundle; - if (bundle==null) throw new IllegalArgumentException("bundle must not be null"); } - protected Segment<K,V> createSegment(Object segmentInfo, int cap) { - ReferenceBundle bundle = (ReferenceBundle) segmentInfo; - if (bundle==null) throw new IllegalArgumentException("bundle must not be null"); - return new ManagedConcurrentMap.Segment<K,V>(bundle, cap); + /** + * Returns the value stored for the given key at the point of call. + * + * @param key a non null key + * @return the value stored in the map for the given key + */ + public V get(Object key) { + return toValue(internalMap.get(new Key(key))); } - public static class Segment<K,V> extends AbstractConcurrentMap.Segment<K,V>{ - private static final long serialVersionUID = 2742952509311037869L; - protected final ReferenceBundle bundle; - public Segment(ReferenceBundle bundle, int cap) { - super(cap); - this.bundle = bundle; - if (bundle==null) throw new IllegalArgumentException("bundle must not be null"); - - } + /** + * Sets a new value for a given key. an older value is overwritten. + * + * @param key a non null key + * @param value the new value + */ + public void put(final K key, V value) { + final Key<K> k = new Key<K>(key); + internalMap.put(k, toManagedReference(k, value)); + } - protected AbstractConcurrentMap.Entry<K,V> createEntry(K key, int hash, V value) { - if (bundle==null) throw new IllegalArgumentException("bundle must not be null"); - return new EntryWithValue<K,V>(bundle, this, key, hash, value); - } + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { + return toValue(getOrPut(key, mappingFunction)); } - public static class Entry<K,V> extends ManagedReference<K> implements AbstractConcurrentMap.Entry<K,V> { - private final Segment segment; - private final int hash; + public ManagedReference<V> getOrPut(K key, V value) { + return getOrPut(key, k -> value); + } - public Entry(ReferenceBundle bundle, Segment segment, K key, int hash) { - super(bundle, key); - this.segment = segment; - this.hash = hash; - } + private ManagedReference<V> getOrPut(K key, Function<? super K, ? extends V> mappingFunction) { + final Key<K> key1 = new Key<K>(key); + return internalMap.computeIfAbsent(key1, k -> toManagedReference(key1, mappingFunction.apply(k.key))); + } - public boolean isValid() { - return get() != null; - } + public V remove(Object key) { + return toValue(internalMap.remove(new Key(key))); + } - public boolean isEqual(K key, int hash) { - return this.hash == hash && get() == key; - } + public Collection<V> values() { + return internalMap.values().stream().map(this::toValue).collect(Collectors.toList()); + } - public V getValue() { - return (V)this; - } + public int size() { + return internalMap.size(); + } - public void setValue(V value) { - } + private V toValue(ManagedReference<V> mv) { + if (null == mv) return null; - public int getHash() { - return hash; - } + return mv.get(); + } - @Override - public void finalizeReference() { - segment.removeEntry(this); - super.finalizeReference(); - } + private ManagedReference<V> toManagedReference(Key<K> key, V value) { + ManagedReference<V> ref = new ManagedReference<V>(bundle, value) { + @Override + public void finalizeReference() { + internalMap.remove(key, this); + super.finalizeReference(); + } + }; - /** - * @deprecated use finalizeReference - */ - @Deprecated - public void finalizeRef() { - finalizeReference(); - } + return ref; } - public static class EntryWithValue<K,V> extends Entry<K,V> { - private V value; - - public EntryWithValue(ReferenceBundle bundle, Segment segment, K key, int hash, V value) { - super(bundle, segment, key, hash); - setValue(value); - } + private static class Key<K> { + K key; - @Override - public V getValue() { - return value; + Key(K key) { + this.key = key; } @Override - public void setValue(V value) { - this.value = value; + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Key)) return false; + Key<?> other = (Key<?>) o; + return key == other.key; } @Override - public void finalizeReference() { - value = null; - super.finalizeReference(); + public int hashCode() { + int hash = System.identityHashCode(key); + hash += (hash << 15) ^ 0xffffcd7d; + hash ^= (hash >>> 10); + hash += (hash << 3); + hash ^= (hash >>> 6); + hash += (hash << 2) + (hash << 14); + hash ^= (hash >>> 16); + return hash; } } } diff --git a/src/test/org/codehaus/groovy/util/AbstractConcurrentMapSegmentTest.groovy b/src/test/org/codehaus/groovy/util/AbstractConcurrentMapSegmentTest.groovy deleted file mode 100644 index 4f275f5..0000000 --- a/src/test/org/codehaus/groovy/util/AbstractConcurrentMapSegmentTest.groovy +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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.util - -import org.junit.Before -import org.junit.Test - -class AbstractConcurrentMapSegmentTest { - private static final Integer INITIAL_SEGMENT_SIZE = 100 - private static final Integer SEGMENT_THRESHOLD = 0.75f * INITIAL_SEGMENT_SIZE - - // Incrementing counter used to generate unique key names for TestEntry objects - // across all test methods in this class - private static int keyId - - TestSegment segment - List<TestEntry> entries = [] - int rehashCount = 0 - - @Before - public void setUp() throws Exception { - segment = new TestSegment(INITIAL_SEGMENT_SIZE) - } - - @Test - public void testSegmentWillNotRehash() { - whenIAddElements(50) - thenRehashHappenedTimes(0) - thenSegmentExpands(false) - } - - @Test - public void testSegmentWillNotRehashEdgeCase() { - whenIAddElements(SEGMENT_THRESHOLD + 1) - thenRehashHappenedTimes(0) - thenSegmentExpands(false) - } - - @Test - public void testSegmentWillRehashAndExpand() { - whenIAddElements(SEGMENT_THRESHOLD + 2) - thenRehashHappenedTimes(1) - thenSegmentExpands(true) - } - - @Test - public void testSegmentWillRehashAndExpandManyTimes() { - int elementCount = (SEGMENT_THRESHOLD + 1 ) * 6 - whenIAddElements(elementCount) - //456 elements fit into segment of size 800, which is 100 * 2 * 2 * 2 - thenSegmentSizeIs(INITIAL_SEGMENT_SIZE * 2 * 2 * 2) - thenRehashHappenedTimes(3) - } - - @Test - public void testSegmentWillRehashWithNoExpansion() { - whenIAddElements(SEGMENT_THRESHOLD) - whenISetElementsAsInvalid(50) - whenIAddElements(50) - thenRehashHappenedTimes(1) - thenSegmentExpands(false) - } - - @Test - public void testSegmentWillRehashAndEventuallyExpand() { - whenIAddElements(SEGMENT_THRESHOLD) - - // 1-st rehash - whenISetElementsAsInvalid(50) - whenIAddElements(50) - thenSegmentExpands(false) - - // 2-nd rehash - whenISetElementsAsInvalid(30) - whenIAddElements(30) - thenSegmentExpands(false) - - // 3-nd rehash - whenISetElementsAsInvalid(20) - whenIAddElements(20) - thenSegmentExpands(false) - - // 4-th rehash with none invalid => expansion: segment * 2 - whenIAddElements(SEGMENT_THRESHOLD) - - thenRehashHappenedTimes(4) - thenSegmentSizeIs(INITIAL_SEGMENT_SIZE * 2) - } - - private void whenIAddElements(int count) { - count.times { - String key = "k:${++keyId}-${it}" - segment.put(key, key.hashCode(), "v${it}") - } - } - - private void whenISetElementsAsInvalid(int count) { - List<TestEntry> validEntires = entries.findAll { it.isValid() } - count.times { - validEntires.get(it).setValid(false) - } - } - - private void thenRehashHappenedTimes(int expectedRehashCount) { - assert rehashCount == expectedRehashCount - } - - private void thenSegmentSizeIs(int expectedSize) { - assert segment.table.length == expectedSize - } - - private void thenSegmentExpands(boolean truth) { - assert segment.table.length > INITIAL_SEGMENT_SIZE == truth - } - - class TestSegment extends org.codehaus.groovy.util.AbstractConcurrentMap.Segment { - - protected TestSegment(int initialCapacity) { - super(initialCapacity) - } - - @Override - protected org.codehaus.groovy.util.AbstractConcurrentMap.Entry createEntry(Object key, int hash, Object value) { - TestEntry entry = new TestEntry(key, hash, value) - entries.add(entry) - return entry - } - - @Override - void rehash() { - rehashCount++ - super.rehash() - } - } -} - -class TestEntry implements org.codehaus.groovy.util.AbstractConcurrentMap.Entry { - Object key - Object value - int hash - boolean valid = true; - - public TestEntry(Object key, int hash, Object value) { - this.key = key - this.hash = hash - this.value = value - } - - @Override - boolean isEqual(Object key, int hash) { - return hash == this.hash && key.equals(this.key) - } - - @Override - Object getValue() { - return value - } - - @Override - void setValue(Object value) { - this.value = value - } - - @Override - int getHash() { - return hash - } - - @Override - boolean isValid() { - return valid - } - - public void setValid(boolean valid) { - this.valid = valid - } -} diff --git a/src/test/org/codehaus/groovy/util/ManagedConcurrentMapTest.groovy b/src/test/org/codehaus/groovy/util/ManagedConcurrentMapTest.groovy index d87704f..357e37d 100644 --- a/src/test/org/codehaus/groovy/util/ManagedConcurrentMapTest.groovy +++ b/src/test/org/codehaus/groovy/util/ManagedConcurrentMapTest.groovy @@ -32,12 +32,10 @@ class ManagedConcurrentMapTest extends GroovyTestCase { } assert map.size() == 5 - assert map.fullSize() == 5 entries*.finalizeReference() assert map.size() == 0 - assert map.fullSize() == 0 } }
