--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java Tue May 03 09:09:57 2016 +0100
@@ -0,0 +1,354 @@
+/*
+ * 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 <K1> the type of 1st key in key pair
+ * @param <K2> the type of 2nd key in key pair
+ * @param <V> the type of value
+ * @author Peter Levart
+ */
+final class WeakPairMap<K1, K2, V> {
+
+ private final ConcurrentHashMap<Pair<K1, K2>, V> map = new ConcurrentHashMap<>();
+ private final ReferenceQueue<Object> 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.
+ * <p>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.
+ * <p>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<? super K1, ? super K2, ? extends V>
+ 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<V> 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<K1, K2> {
+
+ static <K1, K2> Pair<K1, K2> weak(K1 k1, K2 k2,
+ ReferenceQueue<Object> queue) {
+ return new Weak<>(k1, k2, queue);
+ }
+
+ static <K1, K2> Pair<K1, K2> 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:
+ * <pre>{@code
+ *
+ * +-referent-> [K1] +-referent-> [K2]
+ * | |
+ * +----------------+ +----------------+
+ * | Pair.Weak <: |-----peer----->| (anonymous) <: |
+ * | WeakRefPeer, | | WeakRefPeer |
+ * | Pair |<--weakPair()--| |
+ * +----------------+ +----------------+
+ * | ^
+ * | |
+ * +-weakPair()-+
+ *
+ * }</pre>
+ * <p>
+ * 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<K1, K2> extends WeakRefPeer<K1> implements Pair<K1, K2> {
+
+ // saved hash so it can be retrieved after the reference is cleared
+ private final int hash;
+ // link to <K2> peer
+ private final WeakRefPeer<K2> peer;
+
+ Weak(K1 k1, K2 k2, ReferenceQueue<Object> queue) {
+ super(k1, queue);
+ hash = Pair.hashCode(k1, k2);
+ peer = new WeakRefPeer<>(k2, queue) {
+ // link back to <K1> 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<K1, K2> implements Pair<K1, K2> {
+ 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<K> extends WeakReference<K> {
+
+ WeakRefPeer(K k, ReferenceQueue<Object> queue) {
+ super(Objects.requireNonNull(k), queue);
+ }
+
+ /**
+ * @return the {@link Pair.Weak} side of the pair of peers.
+ */
+ abstract Pair.Weak<?, ?> weakPair();
+ }
+}