Skip to content

Commit

Permalink
Use Unsafe.compareAndSwap for writing to Stable field and array
Browse files Browse the repository at this point in the history
Using Unsafe.compareAndSwap prevents issues resulting from multiple
threads coming across uninitialized VarHandle.typesAndInvokers
field and competing to populate the field with their own versions.
Using compareAndSwap to populate the typesAndInvokers'
MethodHandle table also prevents similar issues when multiple
threads come across the same unpopulated slot in the table and
compete to populate it.

Signed-off-by: Nazim Bhuiyan <nubhuiyan@ibm.com>
Co-authored-by: Devin Papineau <devin@ajdmp.ca>
  • Loading branch information
nbhuiyan and jdmpapin committed Feb 28, 2024
1 parent bf83787 commit f7d6940
Showing 1 changed file with 26 additions and 2 deletions.
28 changes: 26 additions & 2 deletions src/java.base/share/classes/java/lang/invoke/VarHandle.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved
* ===========================================================================
*/

package java.lang.invoke;

import java.lang.constant.ClassDesc;
Expand All @@ -39,6 +45,7 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ForceInline;
Expand Down Expand Up @@ -2140,11 +2147,22 @@ static class TypesAndInvokers {
MethodHandle[] methodHandle_table = new MethodHandle[AccessMode.COUNT];
}

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.compareAndExchangeReference(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;
}
Expand All @@ -2154,7 +2172,13 @@ 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.compareAndExchangeReference(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;
}
Expand Down

0 comments on commit f7d6940

Please sign in to comment.