8350549: MethodHandleProxies.WRAPPER_TYPES is not thread-safe

Reviewed-by: jpai, jvernee
This commit is contained in:
Chen Liang 2025-05-02 01:30:47 +00:00
parent c514f135cc
commit bd7c77898a
2 changed files with 32 additions and 5 deletions

View File

@ -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<Class<?>> WRAPPER_TYPES = Collections.newSetFromMap(new WeakHashMap<>());
private static final Set<Class<?>> WRAPPER_TYPES = ReferencedKeySet.create(false, ReferencedKeySet.concurrentHashMapSupplier());
private static final ClassValue<WeakReferenceHolder<Class<?>>> PROXIES = new ClassValue<>() {
@Override
protected WeakReferenceHolder<Class<?>> computeValue(Class<?> intfc) {

View File

@ -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 <T extends Throwable> Closeable throwing(Class<T> clz, T value) {
return asInterfaceInstance(Closeable.class, MethodHandles.throwException(void.class, clz).bindTo(value));
}