--- old/src/java.base/share/classes/java/lang/reflect/WeakCache.java 2016-04-08 10:43:29.002667917 +0200 +++ /dev/null 2016-04-05 09:31:53.373418148 +0200 @@ -1,381 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package java.lang.reflect; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; -import java.util.function.Supplier; - -/** - * Cache mapping pairs of {@code (key, sub-key) -> value}. Keys and values are - * weakly but sub-keys are strongly referenced. Keys are passed directly to - * {@link #get} method which also takes a {@code parameter}. Sub-keys are - * calculated from keys and parameters using the {@code subKeyFactory} function - * passed to the constructor. Values are calculated from keys and parameters - * using the {@code valueFactory} function passed to the constructor. - * Keys can be {@code null} and are compared by identity while sub-keys returned by - * {@code subKeyFactory} or values returned by {@code valueFactory} - * can not be null. Sub-keys are compared using their {@link #equals} method. - * Entries are expunged from cache lazily on each invocation to {@link #get}, - * {@link #containsValue} or {@link #size} methods when the WeakReferences to - * keys are cleared. Cleared WeakReferences to individual values don't cause - * expunging, but such entries are logically treated as non-existent and - * trigger re-evaluation of {@code valueFactory} on request for their - * key/subKey. - * - * @author Peter Levart - * @param type of keys - * @param

type of parameters - * @param type of values - */ -final class WeakCache { - - private final ReferenceQueue refQueue - = new ReferenceQueue<>(); - // the key type is Object for supporting null key - private final ConcurrentMap>> map - = new ConcurrentHashMap<>(); - private final ConcurrentMap, Boolean> reverseMap - = new ConcurrentHashMap<>(); - private final BiFunction subKeyFactory; - private final BiFunction valueFactory; - - /** - * Construct an instance of {@code WeakCache} - * - * @param subKeyFactory a function mapping a pair of - * {@code (key, parameter) -> sub-key} - * @param valueFactory a function mapping a pair of - * {@code (key, parameter) -> value} - * @throws NullPointerException if {@code subKeyFactory} or - * {@code valueFactory} is null. - */ - public WeakCache(BiFunction subKeyFactory, - BiFunction valueFactory) { - this.subKeyFactory = Objects.requireNonNull(subKeyFactory); - this.valueFactory = Objects.requireNonNull(valueFactory); - } - - /** - * Look-up the value through the cache. This always evaluates the - * {@code subKeyFactory} function and optionally evaluates - * {@code valueFactory} function if there is no entry in the cache for given - * pair of (key, subKey) or the entry has already been cleared. - * - * @param key possibly null key - * @param parameter parameter used together with key to create sub-key and - * value (should not be null) - * @return the cached value (never null) - * @throws NullPointerException if {@code parameter} passed in or - * {@code sub-key} calculated by - * {@code subKeyFactory} or {@code value} - * calculated by {@code valueFactory} is null. - */ - public V get(K key, P parameter) { - Objects.requireNonNull(parameter); - - expungeStaleEntries(); - - Object cacheKey = CacheKey.valueOf(key, refQueue); - - // lazily install the 2nd level valuesMap for the particular cacheKey - ConcurrentMap> valuesMap = map.get(cacheKey); - if (valuesMap == null) { - ConcurrentMap> oldValuesMap - = map.putIfAbsent(cacheKey, - valuesMap = new ConcurrentHashMap<>()); - if (oldValuesMap != null) { - valuesMap = oldValuesMap; - } - } - - // create subKey and retrieve the possible Supplier stored by that - // subKey from valuesMap - Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); - Supplier supplier = valuesMap.get(subKey); - Factory factory = null; - - while (true) { - if (supplier != null) { - // supplier might be a Factory or a CacheValue instance - V value = supplier.get(); - if (value != null) { - return value; - } - } - // else no supplier in cache - // or a supplier that returned null (could be a cleared CacheValue - // or a Factory that wasn't successful in installing the CacheValue) - - // lazily construct a Factory - if (factory == null) { - factory = new Factory(key, parameter, subKey, valuesMap); - } - - if (supplier == null) { - supplier = valuesMap.putIfAbsent(subKey, factory); - if (supplier == null) { - // successfully installed Factory - supplier = factory; - } - // else retry with winning supplier - } else { - if (valuesMap.replace(subKey, supplier, factory)) { - // successfully replaced - // cleared CacheEntry / unsuccessful Factory - // with our Factory - supplier = factory; - } else { - // retry with current supplier - supplier = valuesMap.get(subKey); - } - } - } - } - - /** - * Checks whether the specified non-null value is already present in this - * {@code WeakCache}. The check is made using identity comparison regardless - * of whether value's class overrides {@link Object#equals} or not. - * - * @param value the non-null value to check - * @return true if given {@code value} is already cached - * @throws NullPointerException if value is null - */ - public boolean containsValue(V value) { - Objects.requireNonNull(value); - - expungeStaleEntries(); - return reverseMap.containsKey(new LookupValue<>(value)); - } - - /** - * Returns the current number of cached entries that - * can decrease over time when keys/values are GC-ed. - */ - public int size() { - expungeStaleEntries(); - return reverseMap.size(); - } - - @SuppressWarnings("unchecked") // refQueue.poll actually returns CacheKey - private void expungeStaleEntries() { - CacheKey cacheKey; - while ((cacheKey = (CacheKey)refQueue.poll()) != null) { - cacheKey.expungeFrom(map, reverseMap); - } - } - - /** - * A factory {@link Supplier} that implements the lazy synchronized - * construction of the value and installment of it into the cache. - */ - private final class Factory implements Supplier { - - private final K key; - private final P parameter; - private final Object subKey; - private final ConcurrentMap> valuesMap; - - Factory(K key, P parameter, Object subKey, - ConcurrentMap> valuesMap) { - this.key = key; - this.parameter = parameter; - this.subKey = subKey; - this.valuesMap = valuesMap; - } - - @Override - public synchronized V get() { // serialize access - // re-check - Supplier supplier = valuesMap.get(subKey); - if (supplier != this) { - // something changed while we were waiting: - // might be that we were replaced by a CacheValue - // or were removed because of failure -> - // return null to signal WeakCache.get() to retry - // the loop - return null; - } - // else still us (supplier == this) - - // create new value - V value = null; - try { - value = Objects.requireNonNull(valueFactory.apply(key, parameter)); - } finally { - if (value == null) { // remove us on failure - valuesMap.remove(subKey, this); - } - } - // the only path to reach here is with non-null value - assert value != null; - - // wrap value with CacheValue (WeakReference) - CacheValue cacheValue = new CacheValue<>(value); - - // try replacing us with CacheValue (this should always succeed) - if (valuesMap.replace(subKey, this, cacheValue)) { - // put also in reverseMap - reverseMap.put(cacheValue, Boolean.TRUE); - } else { - throw new AssertionError("Should not reach here"); - } - - // successfully replaced us with new CacheValue -> return the value - // wrapped by it - return value; - } - } - - /** - * Common type of value suppliers that are holding a referent. - * The {@link #equals} and {@link #hashCode} of implementations is defined - * to compare the referent by identity. - */ - private interface Value extends Supplier {} - - /** - * An optimized {@link Value} used to look-up the value in - * {@link WeakCache#containsValue} method so that we are not - * constructing the whole {@link CacheValue} just to look-up the referent. - */ - private static final class LookupValue implements Value { - private final V value; - - LookupValue(V value) { - this.value = value; - } - - @Override - public V get() { - return value; - } - - @Override - public int hashCode() { - return System.identityHashCode(value); // compare by identity - } - - @Override - public boolean equals(Object obj) { - return obj == this || - obj instanceof Value && - this.value == ((Value) obj).get(); // compare by identity - } - } - - /** - * A {@link Value} that weakly references the referent. - */ - private static final class CacheValue - extends WeakReference implements Value - { - private final int hash; - - CacheValue(V value) { - super(value); - this.hash = System.identityHashCode(value); // compare by identity - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - V value; - return obj == this || - obj instanceof Value && - // cleared CacheValue is only equal to itself - (value = get()) != null && - value == ((Value) obj).get(); // compare by identity - } - } - - /** - * CacheKey containing a weakly referenced {@code key}. It registers - * itself with the {@code refQueue} so that it can be used to expunge - * the entry when the {@link WeakReference} is cleared. - */ - private static final class CacheKey extends WeakReference { - - // a replacement for null keys - private static final Object NULL_KEY = new Object(); - - static Object valueOf(K key, ReferenceQueue refQueue) { - return key == null - // null key means we can't weakly reference it, - // so we use a NULL_KEY singleton as cache key - ? NULL_KEY - // non-null key requires wrapping with a WeakReference - : new CacheKey<>(key, refQueue); - } - - private final int hash; - - private CacheKey(K key, ReferenceQueue refQueue) { - super(key, refQueue); - this.hash = System.identityHashCode(key); // compare by identity - } - - @Override - public int hashCode() { - return hash; - } - - @Override - @SuppressWarnings("unchecked") - public boolean equals(Object obj) { - K key; - return obj == this || - obj != null && - obj.getClass() == this.getClass() && - // cleared CacheKey is only equal to itself - (key = this.get()) != null && - // compare key by identity - key == ((CacheKey) obj).get(); // Cast is safe from getClass check - } - - void expungeFrom(ConcurrentMap> map, - ConcurrentMap reverseMap) { - // removing just by key is always safe here because after a CacheKey - // is cleared and enqueue-ed it is only equal to itself - // (see equals method)... - ConcurrentMap valuesMap = map.remove(this); - // remove also from reverseMap if needed - if (valuesMap != null) { - for (Object cacheValue : valuesMap.values()) { - reverseMap.remove(cacheValue); - } - } - } - } -}