--- /dev/null 2016-04-20 08:08:33.338582638 +0200 +++ new/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java 2016-04-21 17:34:10.068041285 +0200 @@ -0,0 +1,238 @@ +/* + * 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-reachable keys + * with identity equality semantics to associate a strongly-reachable value. + * Unlike WeakHashMap, this data structure is thread-safe. + * + * @param the type of 1st keys + * @param the type of 2nd keys + * @param the type of values + * + * @author Peter Levart + */ +final class WeakPairMap { + + private final ConcurrentHashMap, V> map = new ConcurrentHashMap<>(); + private final ReferenceQueue queue = new ReferenceQueue<>(); + + public boolean constainsKeyPair(T t, U u) { + expungeStaleAssociations(); + return map.containsKey(Pair.lookup(t, u)); + } + + public V get(T t, U u) { + expungeStaleAssociations(); + return map.get(Pair.lookup(t, u)); + } + + public V put(T t, U u, V v) { + expungeStaleAssociations(); + return map.put(Pair.weak(t, u, queue), v); + } + + public V putIfAbsent(T t, U u, V v) { + expungeStaleAssociations(); + return map.putIfAbsent(Pair.weak(t, u, queue), v); + } + + public V computeIfAbsent(T t, U u, + BiFunction + mappingFunction) { + expungeStaleAssociations(); + try { + return map.computeIfAbsent( + Pair.weak(t, u, queue), + pair -> mappingFunction.apply(pair.first(), pair.second())); + } finally { + Reference.reachabilityFence(t); + Reference.reachabilityFence(u); + } + } + + public Collection values() { + expungeStaleAssociations(); + return map.values(); + } + + private void expungeStaleAssociations() { + Pair.Weak key; + while ((key = (Pair.Weak) queue.poll()) != null) { + // any peer can get cleared and enqueued, but + // only primary peers are keys in the map + map.remove(key.primaryPeer()); + } + } + + private interface Pair { + + static Pair lookup(T t, U u) { + return new Lookup<>(t, u); + } + + static Pair weak(T t, U u, ReferenceQueue queue) { + return new Weak.Primary<>(t, u, queue); + } + + T first(); + + U second(); + + static boolean equals(Pair p1, Pair p2) { + if (p1 == p2) { + return true; + } + Object p1First = p1.first(); + Object p1Second = p1.second(); + return p1First != null && p1Second != null && + p1First == p2.first() && p1Second == p2.second(); + } + + static int hashCode(Object first, Object second) { + return System.identityHashCode(first) ^ + System.identityHashCode(second); + } + + abstract class Weak extends WeakReference implements Pair { + + Weak(T t, ReferenceQueue queue) { + super(Objects.requireNonNull(t), queue); + } + + abstract Primary primaryPeer(); + + abstract Weak peer(); + + @Override + public T first() { + return get(); + } + + @Override + public U second() { + return peer().get(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Pair && + Pair.equals(this, (Pair) obj); + } + + static final class Primary extends Weak { + + final int hash; + final Secondary secondary; + + Primary(T t, U u, ReferenceQueue queue) { + super(t, queue); + hash = Pair.hashCode(t, u); + secondary = new Secondary<>(u, queue, this); + } + + @Override + Primary primaryPeer() { + return this; + } + + @Override + Weak peer() { + return secondary; + } + + @Override + public int hashCode() { + return hash; + } + } + + static final class Secondary extends Weak { + + final Primary primary; + + Secondary(T t, ReferenceQueue queue, Primary primary) { + super(t, queue); + this.primary = primary; + } + + @Override + Primary primaryPeer() { + return primary; + } + + @Override + Weak peer() { + return primary; + } + + @Override + public int hashCode() { + return primary.hash; + } + } + } + + final class Lookup implements Pair { + private final T t; + private final U u; + + Lookup(T t, U u) { + this.t = Objects.requireNonNull(t); + this.u = Objects.requireNonNull(u); + } + + @Override + public T first() { + return t; + } + + @Override + public U second() { + return u; + } + + @Override + public int hashCode() { + return Pair.hashCode(t, u); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Pair && + Pair.equals(this, (Pair) obj); + } + } + } +}