Skip to content

Commit

Permalink
Use Unsafe.compareAndExchange for writing to Stable field and array
Browse files Browse the repository at this point in the history
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 <nubhuiyan@ibm.com>
Co-authored-by: Devin Papineau <devin@ajdmp.ca>
  • Loading branch information
nbhuiyan and jdmpapin committed Feb 29, 2024
1 parent 9f480ba commit 33eb270
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,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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down

0 comments on commit 33eb270

Please sign in to comment.