From 33eb270b41190f1c3736f9fa3deada62c15b30c4 Mon Sep 17 00:00:00 2001 From: Nazim Bhuiyan Date: Thu, 29 Feb 2024 09:30:09 -0500 Subject: [PATCH] Use Unsafe.compareAndExchange for writing to Stable field and array Using Unsafe.compareAndExchange prevents issues resulting from multiple threads coming across uninitialized VarHandle's typesAndInvokers field and competing to populate the field with their own versions. Using compareAndExchange to populate the typesAndInvokers' MethodHandle table also prevents similar issues when multiple threads come across the same unpopulated slot in the MethodHandle table and compete to populate it. Signed-off-by: Nazim Bhuiyan Co-authored-by: Devin Papineau --- .../classes/java/lang/invoke/VarHandle.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/src/java.base/share/classes/java/lang/invoke/VarHandle.java index 37f048b3847..11563d557b8 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -23,9 +23,16 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved + * =========================================================================== + */ + package java.lang.invoke; import jdk.internal.HotSpotIntrinsicCandidate; +import jdk.internal.misc.Unsafe; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; @@ -1964,11 +1971,22 @@ static class TypesAndInvokers { new MethodHandle[AccessMode.values().length]; } + private static final long TYPES_AND_INVOKERS_OFFSET; + + static { + TYPES_AND_INVOKERS_OFFSET = UNSAFE.objectFieldOffset(VarHandle.class, "typesAndInvokers"); + } + @ForceInline private final TypesAndInvokers getTypesAndInvokers() { TypesAndInvokers tis = typesAndInvokers; if (tis == null) { - tis = typesAndInvokers = new TypesAndInvokers(); + tis = new TypesAndInvokers(); + Object other = UNSAFE.compareAndExchangeObject(this, TYPES_AND_INVOKERS_OFFSET, null, tis); + if (other != null) { + // Lost the race, so use what was set by winning thread. + tis = (TypesAndInvokers) other; + } } return tis; } @@ -1978,7 +1996,13 @@ final MethodHandle getMethodHandle(int mode) { TypesAndInvokers tis = getTypesAndInvokers(); MethodHandle mh = tis.methodHandle_table[mode]; if (mh == null) { - mh = tis.methodHandle_table[mode] = getMethodHandleUncached(mode); + mh = getMethodHandleUncached(mode); + long offset = Unsafe.ARRAY_OBJECT_BASE_OFFSET + (Unsafe.ARRAY_OBJECT_INDEX_SCALE * mode); + Object other = UNSAFE.compareAndExchangeObject(tis.methodHandle_table, offset, null, mh); + if (other != null) { + // We lost the race. Use the winning thread's handle instead. + mh = (MethodHandle) other; + } } return mh; }