Skip to content

Commit

Permalink
8310913: Move ReferencedKeyMap to jdk.internal so it may be shared
Browse files Browse the repository at this point in the history
Reviewed-by: rrich
Backport-of: 6af0af593446bc33dc94bbf7334c325c4ac0ac0f
  • Loading branch information
shipilev committed May 7, 2024
1 parent 92b43c5 commit ec6b326
Show file tree
Hide file tree
Showing 11 changed files with 537 additions and 277 deletions.
133 changes: 12 additions & 121 deletions src/java.base/share/classes/java/lang/invoke/MethodType.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Supplier;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -42,7 +44,8 @@
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;

import jdk.internal.access.SharedSecrets;
import jdk.internal.util.ReferencedKeySet;
import jdk.internal.util.ReferenceKey;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyType;
Expand Down Expand Up @@ -227,7 +230,13 @@ private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num
return new IndexOutOfBoundsException(num.toString());
}

static final ConcurrentWeakInternSet<MethodType> internTable = new ConcurrentWeakInternSet<>();
static final ReferencedKeySet<MethodType> internTable =
ReferencedKeySet.create(false, true, new Supplier<>() {
@Override
public Map<ReferenceKey<MethodType>, ReferenceKey<MethodType>> get() {
return new ConcurrentHashMap<>(512);
}
});

static final Class<?>[] NO_PTYPES = {};

Expand Down Expand Up @@ -405,7 +414,7 @@ private static MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean tr
mt = new MethodType(rtype, ptypes);
}
mt.form = MethodTypeForm.findForm(mt);
return internTable.add(mt);
return internTable.intern(mt);
}
private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20];

Expand Down Expand Up @@ -883,10 +892,6 @@ public Class<?>[] parameterArray() {
* @param x object to compare
* @see Object#equals(Object)
*/
// This implementation may also return true if x is a WeakEntry containing
// a method type that is equal to this. This is an internal implementation
// detail to allow for faster method type lookups.
// See ConcurrentWeakInternSet.WeakEntry#equals(Object)
@Override
public boolean equals(Object x) {
if (this == x) {
Expand All @@ -895,12 +900,6 @@ public boolean equals(Object x) {
if (x instanceof MethodType) {
return equals((MethodType)x);
}
if (x instanceof ConcurrentWeakInternSet.WeakEntry) {
Object o = ((ConcurrentWeakInternSet.WeakEntry)x).get();
if (o instanceof MethodType) {
return equals((MethodType)o);
}
}
return false;
}

Expand Down Expand Up @@ -1392,112 +1391,4 @@ private Object readResolve() {
wrapAlt = null;
return mt;
}

/**
* Simple implementation of weak concurrent intern set.
*
* @param <T> interned type
*/
private static class ConcurrentWeakInternSet<T> {

private final ConcurrentMap<WeakEntry<T>, WeakEntry<T>> map;
private final ReferenceQueue<T> stale;

public ConcurrentWeakInternSet() {
this.map = new ConcurrentHashMap<>(512);
this.stale = SharedSecrets.getJavaLangRefAccess().newNativeReferenceQueue();
}

/**
* Get the existing interned element.
* This method returns null if no element is interned.
*
* @param elem element to look up
* @return the interned element
*/
public T get(T elem) {
if (elem == null) throw new NullPointerException();
expungeStaleElements();

WeakEntry<T> value = map.get(elem);
if (value != null) {
T res = value.get();
if (res != null) {
return res;
}
}
return null;
}

/**
* Interns the element.
* Always returns non-null element, matching the one in the intern set.
* Under the race against another add(), it can return <i>different</i>
* element, if another thread beats us to interning it.
*
* @param elem element to add
* @return element that was actually added
*/
public T add(T elem) {
if (elem == null) throw new NullPointerException();

// Playing double race here, and so spinloop is required.
// First race is with two concurrent updaters.
// Second race is with GC purging weak ref under our feet.
// Hopefully, we almost always end up with a single pass.
T interned;
WeakEntry<T> e = new WeakEntry<>(elem, stale);
do {
expungeStaleElements();
WeakEntry<T> exist = map.putIfAbsent(e, e);
interned = (exist == null) ? elem : exist.get();
} while (interned == null);
return interned;
}

private void expungeStaleElements() {
Reference<? extends T> reference;
while ((reference = stale.poll()) != null) {
map.remove(reference);
}
}

private static class WeakEntry<T> extends WeakReference<T> {

public final int hashcode;

public WeakEntry(T key, ReferenceQueue<T> queue) {
super(key, queue);
hashcode = key.hashCode();
}

/**
* This implementation returns {@code true} if {@code obj} is another
* {@code WeakEntry} whose referent is equal to this referent, or
* if {@code obj} is equal to the referent of this. This allows
* lookups to be made without wrapping in a {@code WeakEntry}.
*
* @param obj the object to compare
* @return true if {@code obj} is equal to this or the referent of this
* @see MethodType#equals(Object)
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
Object mine = get();
if (obj instanceof WeakEntry) {
Object that = ((WeakEntry) obj).get();
return (that == null || mine == null) ? (this == obj) : mine.equals(that);
}
return (mine == null) ? (obj == null) : mine.equals(obj);
}

@Override
public int hashCode() {
return hashcode;
}

}
}

}
3 changes: 2 additions & 1 deletion src/java.base/share/classes/java/lang/runtime/Carriers.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.concurrent.ConcurrentHashMap;

import jdk.internal.misc.Unsafe;
import jdk.internal.util.ReferencedKeyMap;

import static java.lang.invoke.MethodType.methodType;

Expand Down Expand Up @@ -366,7 +367,7 @@ MethodHandle[] createComponents(CarrierShape carrierShape) {
* Cache mapping {@link MethodType} to previously defined {@link CarrierElements}.
*/
private static final Map<MethodType, CarrierElements>
methodTypeCache = ReferencedKeyMap.create(ConcurrentHashMap::new);
methodTypeCache = ReferencedKeyMap.create(false, ConcurrentHashMap::new);

/**
* Permute a raw constructor and component accessor {@link MethodHandle MethodHandles} to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public interface JavaLangRefAccess {
/**
* Constructs a new NativeReferenceQueue.
*
* Invoked by MethodType.ConcurrentWeakInternSet
* Invoked by jdk.internal.util.ReferencedKeyMap
*/
<T> ReferenceQueue<T> newNativeReferenceQueue();
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@
* questions.
*/

package java.lang.runtime;
package jdk.internal.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.lang.ref.Reference;

/**
* View/wrapper of keys used by the backing {@link ReferencedKeyMap}.
Expand All @@ -39,11 +36,8 @@
* @param <T> key type
*
* @since 21
*
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
* Do not rely on its availability.
*/
sealed interface ReferenceKey<T> permits StrongReferenceKey, WeakReferenceKey, SoftReferenceKey {
public sealed interface ReferenceKey<T> permits StrongReferenceKey, WeakReferenceKey, SoftReferenceKey {
/**
* {@return the value of the unwrapped key}
*/
Expand Down

1 comment on commit ec6b326

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.