diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 51846e682b6..70592351827 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -35,12 +35,10 @@ import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -55,6 +53,7 @@ import jdk.internal.constant.ConstantUtils; import jdk.internal.loader.ClassLoaders; import jdk.internal.module.Modules; import jdk.internal.util.ClassFileDumper; +import jdk.internal.util.ReferencedKeySet; import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleStatics.*; @@ -200,7 +199,7 @@ public final class MethodHandleProxies { private static final ClassFileDumper DUMPER = ClassFileDumper.getInstance( "jdk.invoke.MethodHandleProxies.dumpClassFiles", "DUMP_MH_PROXY_CLASSFILES"); - private static final Set> WRAPPER_TYPES = Collections.newSetFromMap(new WeakHashMap<>()); + private static final Set> WRAPPER_TYPES = ReferencedKeySet.create(false, ReferencedKeySet.concurrentHashMapSupplier()); private static final ClassValue>> PROXIES = new ClassValue<>() { @Override protected WeakReferenceHolder> computeValue(Class intfc) { diff --git a/test/jdk/java/lang/invoke/MethodHandleProxies/BasicTest.java b/test/jdk/java/lang/invoke/MethodHandleProxies/BasicTest.java index c2ca4bb5682..b02d3031c8a 100644 --- a/test/jdk/java/lang/invoke/MethodHandleProxies/BasicTest.java +++ b/test/jdk/java/lang/invoke/MethodHandleProxies/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -30,6 +30,7 @@ import java.io.IOException; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleProxies; import java.lang.invoke.MethodHandles; import java.lang.invoke.WrongMethodTypeException; import java.lang.reflect.AccessFlag; @@ -45,6 +46,7 @@ import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.IntSupplier; import java.util.function.ToLongFunction; +import java.util.stream.Stream; import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleProxies.*; @@ -55,7 +57,7 @@ import static org.junit.jupiter.api.Assertions.*; /* * @test - * @bug 6983726 8206955 8269351 + * @bug 6983726 8206955 8269351 8350549 * @summary Basic sanity tests for MethodHandleProxies * @build BasicTest Client * @run junit BasicTest @@ -251,6 +253,32 @@ public class BasicTest { assertThrows(ClassCastException.class, proxy::iterator); } + /** + * Verifies {@code isWrapperInstance} works under race and is thread safe + * like {@code Class} objects are. + */ + @Test + public void testRacyWrapperCheck() { + MethodHandle noop = MethodHandles.zero(void.class); + var lookup = MethodHandles.lookup(); + AtomicInteger counter = new AtomicInteger(); + Stream.generate(() -> { + String name = "MHPRaceIface" + counter.getAndIncrement(); + var bytes = ClassFile.of().build(ClassDesc.of(name), clb -> + clb.withFlags(ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE) + .withMethod("sam", MTD_void, ACC_PUBLIC | ACC_ABSTRACT, _ -> {})); + try { + return lookup.defineClass(bytes); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }).parallel() + .map(cl -> MethodHandleProxies.asInterfaceInstance(cl, noop)) + .limit(100) + .forEach(inst -> assertTrue(MethodHandleProxies.isWrapperInstance(inst), + () -> Objects.toIdentityString(inst) + " should pass wrapper test")); + } + private static Closeable throwing(Class clz, T value) { return asInterfaceInstance(Closeable.class, MethodHandles.throwException(void.class, clz).bindTo(value)); }