diff -r 49a5ced535f6 -r a34001e206f9 jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java Thu Apr 13 20:35:38 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2016, 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.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; - -/** - * A WeakHashMap-like data structure that uses a pair of weakly-referenced keys - * with identity equality semantics to associate a strongly-referenced value. - * Unlike WeakHashMap, this data structure is thread-safe. - * - * @param the type of 1st key in key pair - * @param the type of 2nd key in key pair - * @param the type of value - * @author Peter Levart - */ -final class WeakPairMap { - - private final ConcurrentHashMap, V> map = new ConcurrentHashMap<>(); - private final ReferenceQueue queue = new ReferenceQueue<>(); - - /** - * Tests if the specified pair of keys are associated with a value - * in the WeakPairMap. - * - * @param k1 the 1st of the pair of keys - * @param k2 the 2nd of the pair of keys - * @return true if and only if the specified key pair is in this WeakPairMap, - * as determined by the identity comparison; false otherwise - * @throws NullPointerException if any of the specified keys is null - */ - public boolean containsKeyPair(K1 k1, K2 k2) { - expungeStaleAssociations(); - return map.containsKey(Pair.lookup(k1, k2)); - } - - /** - * Returns the value to which the specified pair of keys is mapped, or null - * if this WeakPairMap contains no mapping for the key pair. - *

More formally, if this WeakPairMap contains a mapping from a key pair - * {@code (_k1, _k2)} to a value {@code v} such that - * {@code k1 == _k1 && k2 == _k2}, then this method returns {@code v}; - * otherwise it returns {@code null}. - * (There can be at most one such mapping.) - * - * @param k1 the 1st of the pair of keys for which the mapped value is to - * be returned - * @param k2 the 2nd of the pair of keys for which the mapped value is to - * be returned - * @return the value to which the specified key pair is mapped, or null if - * this map contains no mapping for the key pair - * @throws NullPointerException if any of the specified keys is null - */ - public V get(K1 k1, K2 k2) { - expungeStaleAssociations(); - return map.get(Pair.lookup(k1, k2)); - } - - /** - * Maps the specified key pair to the specified value in this WeakPairMap. - * Neither the keys nor the value can be null. - *

The value can be retrieved by calling the {@link #get} method - * with the the same keys (compared by identity). - * - * @param k1 the 1st of the pair of keys with which the specified value is to - * be associated - * @param k2 the 2nd of the pair of keys with which the specified value is to - * be associated - * @param v value to be associated with the specified key pair - * @return the previous value associated with key pair, or {@code null} if - * there was no mapping for key pair - * @throws NullPointerException if any of the specified keys or value is null - */ - public V put(K1 k1, K2 k2, V v) { - expungeStaleAssociations(); - return map.put(Pair.weak(k1, k2, queue), v); - } - - /** - * If the specified key pair is not already associated with a value, - * associates it with the given value and returns {@code null}, else does - * nothing and returns the currently associated value. - * - * @param k1 the 1st of the pair of keys with which the specified value is to - * be associated - * @param k2 the 2nd of the pair of keys with which the specified value is to - * be associated - * @param v value to be associated with the specified key pair - * @return the previous value associated with key pair, or {@code null} if - * there was no mapping for key pair - * @throws NullPointerException if any of the specified keys or value is null - */ - public V putIfAbsent(K1 k1, K2 k2, V v) { - expungeStaleAssociations(); - return map.putIfAbsent(Pair.weak(k1, k2, queue), v); - } - - /** - * If the specified key pair is not already associated with a value, - * attempts to compute its value using the given mapping function - * and enters it into this WeakPairMap unless {@code null}. The entire - * method invocation is performed atomically, so the function is - * applied at most once per key pair. Some attempted update operations - * on this WeakPairMap by other threads may be blocked while computation - * is in progress, so the computation should be short and simple, - * and must not attempt to update any other mappings of this WeakPairMap. - * - * @param k1 the 1st of the pair of keys with which the - * computed value is to be associated - * @param k2 the 2nd of the pair of keys with which the - * computed value is to be associated - * @param mappingFunction the function to compute a value - * @return the current (existing or computed) value associated with - * the specified key pair, or null if the computed value is null - * @throws NullPointerException if any of the specified keys or - * mappingFunction is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map - * that would otherwise never complete - * @throws RuntimeException or Error if the mappingFunction does so, in - * which case the mapping is left unestablished - */ - public V computeIfAbsent(K1 k1, K2 k2, - BiFunction - mappingFunction) { - expungeStaleAssociations(); - try { - return map.computeIfAbsent( - Pair.weak(k1, k2, queue), - pair -> mappingFunction.apply(pair.first(), pair.second())); - } finally { - Reference.reachabilityFence(k1); - Reference.reachabilityFence(k2); - } - } - - /** - * Returns a {@link Collection} view of the values contained in this - * WeakPairMap. The collection is backed by the WeakPairMap, so changes to - * the map are reflected in the collection, and vice-versa. The collection - * supports element removal, which removes the corresponding - * mapping from this map, via the {@code Iterator.remove}, - * {@code Collection.remove}, {@code removeAll}, - * {@code retainAll}, and {@code clear} operations. It does not - * support the {@code add} or {@code addAll} operations. - * - * @return the collection view - */ - public Collection values() { - expungeStaleAssociations(); - return map.values(); - } - - /** - * Removes associations from this WeakPairMap for which at least one of the - * keys in key pair has been found weakly-reachable and corresponding - * WeakRefPeer(s) enqueued. Called as part of each public operation. - */ - private void expungeStaleAssociations() { - WeakRefPeer peer; - while ((peer = (WeakRefPeer) queue.poll()) != null) { - map.remove(peer.weakPair()); - } - } - - /** - * Common interface of both {@link Weak} and {@link Lookup} key pairs. - */ - private interface Pair { - - static Pair weak(K1 k1, K2 k2, - ReferenceQueue queue) { - return new Weak<>(k1, k2, queue); - } - - static Pair lookup(K1 k1, K2 k2) { - return new Lookup<>(k1, k2); - } - - /** - * @return The 1st of the pair of keys (may be null for {@link Weak} - * when it gets cleared) - */ - K1 first(); - - /** - * @return The 2nd of the pair of keys (may be null for {@link Weak} - * when it gets cleared) - */ - K2 second(); - - static int hashCode(Object first, Object second) { - // assert first != null && second != null; - return System.identityHashCode(first) ^ - System.identityHashCode(second); - } - - static boolean equals(Object first, Object second, Pair p) { - return first != null && second != null && - first == p.first() && second == p.second(); - } - - /** - * A Pair where both keys are weakly-referenced. - * It is composed of two instances of {@link WeakRefPeer}s: - *
{@code
-         *
-         *     +-referent-> [K1]                +-referent-> [K2]
-         *     |                                |
-         *   +----------------+               +----------------+
-         *   | Pair.Weak <:   |-----peer----->| (anonymous) <: |
-         *   | WeakRefPeer,   |               | WeakRefPeer    |
-         *   | Pair           |<--weakPair()--|                |
-         *   +----------------+               +----------------+
-         *     |            ^
-         *     |            |
-         *     +-weakPair()-+
-         *
-         * }
- *

- * Pair.Weak is used for CHM keys. Both peers are associated with the - * same {@link ReferenceQueue} so when either of their referents - * becomes weakly-reachable, the corresponding entries can be - * {@link #expungeStaleAssociations() expunged} from the map. - */ - final class Weak extends WeakRefPeer implements Pair { - - // saved hash so it can be retrieved after the reference is cleared - private final int hash; - // link to peer - private final WeakRefPeer peer; - - Weak(K1 k1, K2 k2, ReferenceQueue queue) { - super(k1, queue); - hash = Pair.hashCode(k1, k2); - peer = new WeakRefPeer<>(k2, queue) { - // link back to peer - @Override - Weak weakPair() { return Weak.this; } - }; - } - - @Override - Weak weakPair() { - return this; - } - - @Override - public K1 first() { - return get(); - } - - @Override - public K2 second() { - return peer.get(); - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - return this == obj || - (obj instanceof Pair && - Pair.equals(first(), second(), (Pair) obj)); - } - } - - /** - * Optimized lookup Pair, used as lookup key in methods like - * {@link java.util.Map#get(Object)} or - * {@link java.util.Map#containsKey(Object)}) where - * there is a great chance its allocation is eliminated - * by escape analysis when such lookups are inlined by JIT. - * All its methods are purposely designed so that 'this' is never - * passed to any other method or used as identity. - */ - final class Lookup implements Pair { - private final K1 k1; - private final K2 k2; - - Lookup(K1 k1, K2 k2) { - this.k1 = Objects.requireNonNull(k1); - this.k2 = Objects.requireNonNull(k2); - } - - @Override - public K1 first() { - return k1; - } - - @Override - public K2 second() { - return k2; - } - - @Override - public int hashCode() { - return Pair.hashCode(k1, k2); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Pair && - Pair.equals(k1, k2, (Pair) obj); - } - } - } - - /** - * Common abstract supertype of a pair of WeakReference peers. - */ - private static abstract class WeakRefPeer extends WeakReference { - - WeakRefPeer(K k, ReferenceQueue queue) { - super(Objects.requireNonNull(k), queue); - } - - /** - * @return the {@link Pair.Weak} side of the pair of peers. - */ - abstract Pair.Weak weakPair(); - } -}