Avoid Unsafe usage where possible (#2403)

This commit is contained in:
Hannes Greule 2023-09-09 16:07:37 +02:00 committed by GitHub
parent 60a3994d62
commit f36c5d42c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 43 additions and 111 deletions

View File

@ -85,11 +85,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; private static final MethodHandle methodGetVisibleChunk;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final Field fieldGameEventDispatcherSections; private static final Field fieldGameEventDispatcherSections;
private static final MethodHandle methodremoveBlockEntityTicker; private static final MethodHandle methodremoveBlockEntityTicker;
@ -127,15 +123,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m")); fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName( fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName(
@ -152,13 +145,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
fieldRemove.setAccessible(true); fieldRemove.setAccessible(true);
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
if ((scale & (scale - 1)) != 0) {
throw new Error("data type scale not a power of two");
}
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -173,9 +159,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -190,14 +175,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
Semaphore currentLock = (Semaphore) unsafe.getObject(blocks, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(blocks);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(blocks, fieldLockOffset, newLock); fieldLock.set(blocks, newLock);
return newLock; return newLock;
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -329,7 +329,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
}; };
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider); chunkSourceField.set(freshWorld, freshChunkProvider);
//let's start then //let's start then
structureManager = server.getStructureManager(); structureManager = server.getStructureManager();
threadedLevelLightEngine = freshChunkProvider.getLightEngine(); threadedLevelLightEngine = freshChunkProvider.getLightEngine();

View File

@ -92,14 +92,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; private static final MethodHandle methodGetVisibleChunk;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
private static final Field fieldThreadingDetector; private static final Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -136,20 +130,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -169,13 +158,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
fieldRemove.setAccessible(true); fieldRemove.setAccessible(true);
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
if ((scale & (scale - 1)) != 0) {
throw new Error("data type scale not a power of two");
}
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -190,9 +172,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -207,19 +188,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -342,7 +342,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
}; };
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider); chunkSourceField.set(freshWorld, freshChunkProvider);
//let's start then //let's start then
structureManager = server.getStructureManager(); structureManager = server.getStructureManager();
threadedLevelLightEngine = freshChunkProvider.getLightEngine(); threadedLevelLightEngine = freshChunkProvider.getLightEngine();

View File

@ -99,14 +99,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; private static final MethodHandle methodGetVisibleChunk;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
private static final Field fieldThreadingDetector; private static final Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -149,20 +143,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -185,12 +174,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
fieldRemove.setAccessible(true); fieldRemove.setAccessible(true);
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
if ((scale & (scale - 1)) != 0) {
throw new Error("data type scale not a power of two");
}
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
boolean chunkRewrite; boolean chunkRewrite;
try { try {
ServerLevel.class.getDeclaredMethod("getEntityLookup"); ServerLevel.class.getDeclaredMethod("getEntityLookup");
@ -226,9 +209,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -243,19 +225,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -369,7 +369,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider); chunkSourceField.set(freshWorld, freshChunkProvider);
//let's start then //let's start then
structureTemplateManager = server.getStructureManager(); structureTemplateManager = server.getStructureManager();
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);

View File

@ -102,14 +102,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; private static final MethodHandle methodGetVisibleChunk;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
private static final Field fieldThreadingDetector; private static final Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -158,20 +152,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -194,12 +183,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
fieldRemove.setAccessible(true); fieldRemove.setAccessible(true);
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
if ((scale & (scale - 1)) != 0) {
throw new Error("data type scale not a power of two");
}
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
boolean chunkRewrite; boolean chunkRewrite;
try { try {
ServerLevel.class.getDeclaredMethod("getEntityLookup"); ServerLevel.class.getDeclaredMethod("getEntityLookup");
@ -249,9 +232,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -266,19 +248,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -370,7 +370,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider); chunkSourceField.set(freshWorld, freshChunkProvider);
//let's start then //let's start then
structureTemplateManager = server.getStructureManager(); structureTemplateManager = server.getStructureManager();
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);

View File

@ -4,19 +4,19 @@ import sun.misc.Unsafe;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
/** /**
* This is an internal class not meant to be used outside the FAWE internals. * This is an internal class not meant to be used outside the FAWE internals.
*/ */
public class ReflectionUtils { public class ReflectionUtils {
private static final VarHandle REFERENCE_ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(Object[].class);
private static Unsafe UNSAFE; private static Unsafe UNSAFE;
static { static {
@ -33,6 +33,21 @@ public class ReflectionUtils {
return t.isInstance(o) ? t.cast(o) : null; return t.isInstance(o) ? t.cast(o) : null;
} }
/**
* Performs CAS on the array element at the given index.
*
* @param array the array in which to compare and set the value
* @param expectedValue the value expected to be at the index
* @param newValue the new value to be set at the index if the expected value matches
* @param index the index at which to compare and set the value
* @param <T> the type of elements in the array
* @return true if the value at the index was successfully updated to the new value, false otherwise
* @see VarHandle#compareAndSet(Object...)
*/
public static <T> boolean compareAndSet(T[] array, T expectedValue, T newValue, int index) {
return REFERENCE_ARRAY_HANDLE.compareAndSet(array, index, expectedValue, newValue);
}
public static void setAccessibleNonFinal(Field field) { public static void setAccessibleNonFinal(Field field) {
// let's make the field accessible // let's make the field accessible
field.setAccessible(true); field.setAccessible(true);